home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / iceweasel / modules / gcli.jsm < prev    next >
Encoding:
Text File  |  2013-01-09  |  200.3 KB  |  7,079 lines

  1. /*
  2.  * Copyright 2009-2011 Mozilla Foundation and contributors
  3.  * Licensed under the New BSD license. See LICENSE.txt or:
  4.  * http://opensource.org/licenses/BSD-3-Clause
  5.  */
  6.  
  7. /*
  8.  *
  9.  *
  10.  *
  11.  *
  12.  *
  13.  *
  14.  *
  15.  *********************************** WARNING ***********************************
  16.  *
  17.  * Do not edit this file without understanding where it comes from,
  18.  * Your changes are likely to be overwritten without warning.
  19.  *
  20.  * For more information on GCLI see:
  21.  * - https://github.com/mozilla/gcli/blob/master/docs/index.md
  22.  * - https://wiki.mozilla.org/DevTools/Features/GCLI
  23.  *
  24.  * The original source for this file is:
  25.  *  https://github.com/mozilla/gcli/
  26.  *
  27.  * This build of GCLI for Firefox comes from 4 bits of code:
  28.  * - prefix-gcli.jsm: Initial commentary and EXPORTED_SYMBOLS
  29.  * - console.js: Support code common to web content that is not part of the
  30.  *   default firefox chrome environment and is easy to shim.
  31.  * - mini_require: A very basic commonjs AMD (Asynchronous Modules Definition)
  32.  *   'require' implementation (which is just good enough to load GCLI). For
  33.  *   more, see http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition.
  34.  *   This alleviates the need for requirejs (http://requirejs.org/) which is
  35.  *   used when running in the browser. This code is provided by dryice.
  36.  * - A build of GCLI itself, packaged using dryice
  37.  * - suffix-gcli.jsm - code to require the gcli object for EXPORTED_SYMBOLS.
  38.  *
  39.  * See Makefile.dryice.js for more details of this build.
  40.  * For more details on dryice, see the https://github.com/mozilla/dryice
  41.  *
  42.  *******************************************************************************
  43.  *
  44.  *
  45.  *
  46.  *
  47.  *
  48.  *
  49.  *
  50.  *
  51.  *
  52.  */
  53.  
  54. ///////////////////////////////////////////////////////////////////////////////
  55.  
  56. var EXPORTED_SYMBOLS = [ "gcli" ];
  57.  
  58.  
  59. /**
  60.  * Expose a Node object. This allows us to use the Node constants without
  61.  * resorting to hardcoded numbers
  62.  */
  63. var Node = Components.interfaces.nsIDOMNode;
  64.  
  65.  
  66. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  67.  
  68. /**
  69.  * Define setTimeout and clearTimeout to match the browser functions
  70.  */
  71. var setTimeout;
  72. var clearTimeout;
  73.  
  74. (function() {
  75.   /**
  76.    * The next value to be returned by setTimeout
  77.    */
  78.   var nextID = 1;
  79.  
  80.   /**
  81.    * The map of outstanding timeouts
  82.    */
  83.   var timers = {};
  84.  
  85.   /**
  86.    * Object to be passed to Timer.initWithCallback()
  87.    */
  88.   function TimerCallback(callback) {
  89.     this._callback = callback;
  90.     var interfaces = [ Components.interfaces.nsITimerCallback ];
  91.     this.QueryInterface = XPCOMUtils.generateQI(interfaces);
  92.   }
  93.  
  94.   TimerCallback.prototype.notify = function(timer) {
  95.     try {
  96.       for (var timerID in timers) {
  97.         if (timers[timerID] === timer) {
  98.           delete timers[timerID];
  99.           break;
  100.         }
  101.       }
  102.       this._callback.apply(null, []);
  103.     }
  104.     catch (ex) {
  105.       console.error(ex);
  106.     }
  107.   };
  108.  
  109.   /**
  110.    * Executes a code snippet or a function after specified delay.
  111.    * This is designed to have the same interface contract as the browser
  112.    * function.
  113.    * @param callback is the function you want to execute after the delay.
  114.    * @param delay is the number of milliseconds that the function call should
  115.    * be delayed by. Note that the actual delay may be longer, see Notes below.
  116.    * @return the ID of the timeout, which can be used later with
  117.    * window.clearTimeout.
  118.    */
  119.   setTimeout = function setTimeout(callback, delay) {
  120.     var timer = Components.classes["@mozilla.org/timer;1"]
  121.                           .createInstance(Components.interfaces.nsITimer);
  122.  
  123.     var timerID = nextID++;
  124.     timers[timerID] = timer;
  125.  
  126.     timer.initWithCallback(new TimerCallback(callback), delay, timer.TYPE_ONE_SHOT);
  127.     return timerID;
  128.   };
  129.  
  130.   /**
  131.    * Clears the delay set by window.setTimeout() and prevents the callback from
  132.    * being executed (if it hasn't been executed already)
  133.    * @param timerID the ID of the timeout you wish to clear, as returned by
  134.    * window.setTimeout().
  135.    */
  136.   clearTimeout = function clearTimeout(timerID) {
  137.     var timer = timers[timerID];
  138.     if (timer) {
  139.       timer.cancel();
  140.       delete timers[timerID];
  141.     }
  142.   };
  143. })();
  144.  
  145.  
  146. /**
  147.  * This creates a console object that somewhat replicates Firebug's console
  148.  * object. It currently writes to dump(), but should write to the web
  149.  * console's chrome error section (when it has one)
  150.  */
  151. var console = {};
  152. (function() {
  153.   /**
  154.    * String utility to ensure that strings are a specified length. Strings
  155.    * that are too long are truncated to the max length and the last char is
  156.    * set to "_". Strings that are too short are left padded with spaces.
  157.    *
  158.    * @param {string} aStr
  159.    *        The string to format to the correct length
  160.    * @param {number} aMaxLen
  161.    *        The maximum allowed length of the returned string
  162.    * @param {number} aMinLen (optional)
  163.    *        The minimum allowed length of the returned string. If undefined,
  164.    *        then aMaxLen will be used
  165.    * @param {object} aOptions (optional)
  166.    *        An object allowing format customization. The only customization
  167.    *        allowed currently is 'truncate' which can take the value "start" to
  168.    *        truncate strings from the start as opposed to the end.
  169.    * @return {string}
  170.    *        The original string formatted to fit the specified lengths
  171.    */
  172.   function fmt(aStr, aMaxLen, aMinLen, aOptions) {
  173.     if (aMinLen == null) {
  174.       aMinLen = aMaxLen;
  175.     }
  176.     if (aStr == null) {
  177.       aStr = "";
  178.     }
  179.     if (aStr.length > aMaxLen) {
  180.       if (aOptions && aOptions.truncate == "start") {
  181.         return "_" + aStr.substring(aStr.length - aMaxLen + 1);
  182.       }
  183.       else {
  184.         return aStr.substring(0, aMaxLen - 1) + "_";
  185.       }
  186.     }
  187.     if (aStr.length < aMinLen) {
  188.       return Array(aMinLen - aStr.length + 1).join(" ") + aStr;
  189.     }
  190.     return aStr;
  191.   }
  192.  
  193.   /**
  194.    * Utility to extract the constructor name of an object.
  195.    * Object.toString gives: "[object ?????]"; we want the "?????".
  196.    *
  197.    * @param {object} aObj
  198.    *        The object from which to extract the constructor name
  199.    * @return {string}
  200.    *        The constructor name
  201.    */
  202.   function getCtorName(aObj) {
  203.     return Object.prototype.toString.call(aObj).slice(8, -1);
  204.   }
  205.  
  206.   /**
  207.    * A single line stringification of an object designed for use by humans
  208.    *
  209.    * @param {any} aThing
  210.    *        The object to be stringified
  211.    * @return {string}
  212.    *        A single line representation of aThing, which will generally be at
  213.    *        most 80 chars long
  214.    */
  215.   function stringify(aThing) {
  216.     if (aThing === undefined) {
  217.       return "undefined";
  218.     }
  219.  
  220.     if (aThing === null) {
  221.       return "null";
  222.     }
  223.  
  224.     if (typeof aThing == "object") {
  225.       var type = getCtorName(aThing);
  226.       if (type == "XULElement") {
  227.         return debugElement(aThing);
  228.       }
  229.       type = (type == "Object" ? "" : type + " ");
  230.       var json;
  231.       try {
  232.         json = JSON.stringify(aThing);
  233.       }
  234.       catch (ex) {
  235.         // Can't use a real ellipsis here, because cmd.exe isn't unicode-enabled
  236.         json = "{" + Object.keys(aThing).join(":..,") + ":.., " + "}";
  237.       }
  238.       return type + fmt(json, 50, 0);
  239.     }
  240.  
  241.     var str = aThing.toString(); //.replace(/\s+/g, " ");
  242.     return fmt(str, 80, 0);
  243.   }
  244.  
  245.   /**
  246.    * Create a simple debug representation of a given element.
  247.    *
  248.    * @param {nsIDOMElement} aElement
  249.    *        The element to debug
  250.    * @return {string}
  251.    *        A simple single line representation of aElement
  252.    */
  253.   function debugElement(aElement) {
  254.     return "<" + aElement.tagName +
  255.         (aElement.id ? "#" + aElement.id : "") +
  256.         (aElement.className ?
  257.             "." + aElement.className.split(" ").join(" .") :
  258.             "") +
  259.         ">";
  260.   }
  261.  
  262.   /**
  263.    * A multi line stringification of an object, designed for use by humans
  264.    *
  265.    * @param {any} aThing
  266.    *        The object to be stringified
  267.    * @return {string}
  268.    *        A multi line representation of aThing
  269.    */
  270.   function log(aThing) {
  271.     if (aThing === null) {
  272.       return "null\n";
  273.     }
  274.  
  275.     if (aThing === undefined) {
  276.       return "undefined\n";
  277.     }
  278.  
  279.     if (typeof aThing == "object") {
  280.       var reply = "";
  281.       var type = getCtorName(aThing);
  282.       if (type == "Error") {
  283.         reply += "  " + aThing.message + "\n";
  284.         reply += logProperty("stack", aThing.stack);
  285.       }
  286.       else if (type == "XULElement") {
  287.         reply += "  " + debugElement(aThing) + " (XUL)\n";
  288.       }
  289.       else {
  290.         var keys = Object.getOwnPropertyNames(aThing);
  291.         if (keys.length > 0) {
  292.           reply += type + "\n";
  293.           keys.forEach(function(aProp) {
  294.             reply += logProperty(aProp, aThing[aProp]);
  295.           }, this);
  296.         }
  297.         else {
  298.           reply += type + " (enumerated with for-in)\n";
  299.           var prop;
  300.           for (prop in aThing) {
  301.             reply += logProperty(prop, aThing[prop]);
  302.           }
  303.         }
  304.       }
  305.  
  306.       return reply;
  307.     }
  308.  
  309.     return "  " + aThing.toString() + "\n";
  310.   }
  311.  
  312.   /**
  313.    * Helper for log() which converts a property/value pair into an output
  314.    * string
  315.    *
  316.    * @param {string} aProp
  317.    *        The name of the property to include in the output string
  318.    * @param {object} aValue
  319.    *        Value assigned to aProp to be converted to a single line string
  320.    * @return {string}
  321.    *        Multi line output string describing the property/value pair
  322.    */
  323.   function logProperty(aProp, aValue) {
  324.     var reply = "";
  325.     if (aProp == "stack" && typeof value == "string") {
  326.       var trace = parseStack(aValue);
  327.       reply += formatTrace(trace);
  328.     }
  329.     else {
  330.       reply += "    - " + aProp + " = " + stringify(aValue) + "\n";
  331.     }
  332.     return reply;
  333.   }
  334.  
  335.   /**
  336.    * Parse a stack trace, returning an array of stack frame objects, where
  337.    * each has file/line/call members
  338.    *
  339.    * @param {string} aStack
  340.    *        The serialized stack trace
  341.    * @return {object[]}
  342.    *        Array of { file: "...", line: NNN, call: "..." } objects
  343.    */
  344.   function parseStack(aStack) {
  345.     var trace = [];
  346.     aStack.split("\n").forEach(function(line) {
  347.       if (!line) {
  348.         return;
  349.       }
  350.       var at = line.lastIndexOf("@");
  351.       var posn = line.substring(at + 1);
  352.       trace.push({
  353.         file: posn.split(":")[0],
  354.         line: posn.split(":")[1],
  355.         call: line.substring(0, at)
  356.       });
  357.     }, this);
  358.     return trace;
  359.   }
  360.  
  361.   /**
  362.    * parseStack() takes output from an exception from which it creates the an
  363.    * array of stack frame objects, this has the same output but using data from
  364.    * Components.stack
  365.    *
  366.    * @param {string} aFrame
  367.    *        The stack frame from which to begin the walk
  368.    * @return {object[]}
  369.    *        Array of { file: "...", line: NNN, call: "..." } objects
  370.    */
  371.   function getStack(aFrame) {
  372.     if (!aFrame) {
  373.       aFrame = Components.stack.caller;
  374.     }
  375.     var trace = [];
  376.     while (aFrame) {
  377.       trace.push({
  378.         file: aFrame.filename,
  379.         line: aFrame.lineNumber,
  380.         call: aFrame.name
  381.       });
  382.       aFrame = aFrame.caller;
  383.     }
  384.     return trace;
  385.   }
  386.  
  387.   /**
  388.    * Take the output from parseStack() and convert it to nice readable
  389.    * output
  390.    *
  391.    * @param {object[]} aTrace
  392.    *        Array of trace objects as created by parseStack()
  393.    * @return {string} Multi line report of the stack trace
  394.    */
  395.   function formatTrace(aTrace) {
  396.     var reply = "";
  397.     aTrace.forEach(function(frame) {
  398.       reply += fmt(frame.file, 20, 20, { truncate: "start" }) + " " +
  399.                fmt(frame.line, 5, 5) + " " +
  400.                fmt(frame.call, 75, 75) + "\n";
  401.     });
  402.     return reply;
  403.   }
  404.  
  405.   /**
  406.    * Create a function which will output a concise level of output when used
  407.    * as a logging function
  408.    *
  409.    * @param {string} aLevel
  410.    *        A prefix to all output generated from this function detailing the
  411.    *        level at which output occurred
  412.    * @return {function}
  413.    *        A logging function
  414.    * @see createMultiLineDumper()
  415.    */
  416.   function createDumper(aLevel) {
  417.     return function() {
  418.       var args = Array.prototype.slice.call(arguments, 0);
  419.       var data = args.map(function(arg) {
  420.         return stringify(arg);
  421.       });
  422.       dump(aLevel + ": " + data.join(", ") + "\n");
  423.     };
  424.   }
  425.  
  426.   /**
  427.    * Create a function which will output more detailed level of output when
  428.    * used as a logging function
  429.    *
  430.    * @param {string} aLevel
  431.    *        A prefix to all output generated from this function detailing the
  432.    *        level at which output occurred
  433.    * @return {function}
  434.    *        A logging function
  435.    * @see createDumper()
  436.    */
  437.   function createMultiLineDumper(aLevel) {
  438.     return function() {
  439.       dump(aLevel + "\n");
  440.       var args = Array.prototype.slice.call(arguments, 0);
  441.       args.forEach(function(arg) {
  442.         dump(log(arg));
  443.       });
  444.     };
  445.   }
  446.  
  447.   /**
  448.    * Build out the console object
  449.    */
  450.   console.debug = createMultiLineDumper("debug");
  451.   console.log = createDumper("log");
  452.   console.info = createDumper("info");
  453.   console.warn = createDumper("warn");
  454.   console.error = createMultiLineDumper("error");
  455.   console.trace = function Console_trace() {
  456.     var trace = getStack(Components.stack.caller);
  457.     dump(formatTrace(trace) + "\n");
  458.   },
  459.   console.clear = function Console_clear() {};
  460.  
  461.   console.dir = createMultiLineDumper("dir");
  462.   console.dirxml = createMultiLineDumper("dirxml");
  463.   console.group = createDumper("group");
  464.   console.groupEnd = createDumper("groupEnd");
  465.  
  466. })();
  467. /*
  468.  * Copyright 2009-2011 Mozilla Foundation and contributors
  469.  * Licensed under the New BSD license. See LICENSE.txt or:
  470.  * http://opensource.org/licenses/BSD-3-Clause
  471.  */
  472.  
  473.  
  474. /**
  475.  * Define a module along with a payload.
  476.  * @param moduleName Name for the payload
  477.  * @param deps Ignored. For compatibility with CommonJS AMD Spec
  478.  * @param payload Function with (require, exports, module) params
  479.  */
  480. function define(moduleName, deps, payload) {
  481.   if (typeof moduleName != "string") {
  482.     console.error(this.depth + " Error: Module name is not a string.");
  483.     console.trace();
  484.     return;
  485.   }
  486.  
  487.   if (arguments.length == 2) {
  488.     payload = deps;
  489.   }
  490.  
  491.   if (define.debugDependencies) {
  492.     console.log("define: " + moduleName + " -> " + payload.toString()
  493.         .slice(0, 40).replace(/\n/, '\\n').replace(/\r/, '\\r') + "...");
  494.   }
  495.  
  496.   if (moduleName in define.modules) {
  497.     console.error(this.depth + " Error: Redefining module: " + moduleName);
  498.   }
  499.   define.modules[moduleName] = payload;
  500. }
  501.  
  502. /**
  503.  * The global store of un-instantiated modules
  504.  */
  505. define.modules = {};
  506.  
  507. /**
  508.  * Should we console.log on module definition/instantiation/requirement?
  509.  */
  510. define.debugDependencies = false;
  511.  
  512.  
  513. /**
  514.  * Self executing function in which Domain is defined, and attached to define
  515.  */
  516. (function() {
  517.   /**
  518.    * We invoke require() in the context of a Domain so we can have multiple
  519.    * sets of modules running separate from each other.
  520.    * This contrasts with JSMs which are singletons, Domains allows us to
  521.    * optionally load a CommonJS module twice with separate data each time.
  522.    * Perhaps you want 2 command lines with a different set of commands in each,
  523.    * for example.
  524.    */
  525.   function Domain() {
  526.     this.modules = {};
  527.  
  528.     if (define.debugDependencies) {
  529.       this.depth = "";
  530.     }
  531.   }
  532.  
  533.   /**
  534.    * Lookup module names and resolve them by calling the definition function if
  535.    * needed.
  536.    * There are 2 ways to call this, either with an array of dependencies and a
  537.    * callback to call when the dependencies are found (which can happen
  538.    * asynchronously in an in-page context) or with a single string an no
  539.    * callback where the dependency is resolved synchronously and returned.
  540.    * The API is designed to be compatible with the CommonJS AMD spec and
  541.    * RequireJS.
  542.    * @param deps A name, or array of names for the payload
  543.    * @param callback Function to call when the dependencies are resolved
  544.    * @return The module required or undefined for array/callback method
  545.    */
  546.   Domain.prototype.require = function(deps, callback) {
  547.     if (Array.isArray(deps)) {
  548.       var params = deps.map(function(dep) {
  549.         return this.lookup(dep);
  550.       }, this);
  551.       if (callback) {
  552.         callback.apply(null, params);
  553.       }
  554.       return undefined;
  555.     }
  556.     else {
  557.       return this.lookup(deps);
  558.     }
  559.   };
  560.  
  561.   /**
  562.    * Lookup module names and resolve them by calling the definition function if
  563.    * needed.
  564.    * @param moduleName A name for the payload to lookup
  565.    * @return The module specified by aModuleName or null if not found
  566.    */
  567.   Domain.prototype.lookup = function(moduleName) {
  568.     if (moduleName in this.modules) {
  569.       var module = this.modules[moduleName];
  570.       if (define.debugDependencies) {
  571.         console.log(this.depth + " Using module: " + moduleName);
  572.       }
  573.       return module;
  574.     }
  575.  
  576.     if (!(moduleName in define.modules)) {
  577.       console.error(this.depth + " Missing module: " + moduleName);
  578.       return null;
  579.     }
  580.  
  581.     var module = define.modules[moduleName];
  582.  
  583.     if (define.debugDependencies) {
  584.       console.log(this.depth + " Compiling module: " + moduleName);
  585.     }
  586.  
  587.     if (typeof module == "function") {
  588.       if (define.debugDependencies) {
  589.         this.depth += ".";
  590.       }
  591.  
  592.       var exports = {};
  593.       try {
  594.         module(this.require.bind(this), exports, { id: moduleName, uri: "" });
  595.       }
  596.       catch (ex) {
  597.         console.error("Error using module: " + moduleName, ex);
  598.         throw ex;
  599.       }
  600.       module = exports;
  601.  
  602.       if (define.debugDependencies) {
  603.         this.depth = this.depth.slice(0, -1);
  604.       }
  605.     }
  606.  
  607.     // cache the resulting module object for next time
  608.     this.modules[moduleName] = module;
  609.  
  610.     return module;
  611.   };
  612.  
  613.   /**
  614.    * Expose the Domain constructor and a global domain (on the define function
  615.    * to avoid exporting more than we need. This is a common pattern with
  616.    * require systems)
  617.    */
  618.   define.Domain = Domain;
  619.   define.globalDomain = new Domain();
  620. })();
  621.  
  622. /**
  623.  * Expose a default require function which is the require of the global
  624.  * sandbox to make it easy to use.
  625.  */
  626. var require = define.globalDomain.require.bind(define.globalDomain);
  627. /*
  628.  * Copyright 2009-2011 Mozilla Foundation and contributors
  629.  * Licensed under the New BSD license. See LICENSE.txt or:
  630.  * http://opensource.org/licenses/BSD-3-Clause
  631.  */
  632.  
  633. var mozl10n = {};
  634.  
  635. (function(aMozl10n) {
  636.   var temp = {};
  637.   Components.utils.import("resource://gre/modules/Services.jsm", temp);
  638.   var stringBundle = temp.Services.strings.createBundle(
  639.           "chrome://browser/locale/devtools/gclicommands.properties");
  640.  
  641.   /**
  642.    * Lookup a string in the GCLI string bundle
  643.    * @param name The name to lookup
  644.    * @return The looked up name
  645.    */
  646.   aMozl10n.lookup = function(name) {
  647.     try {
  648.       return stringBundle.GetStringFromName(name);
  649.     }
  650.     catch (ex) {
  651.       throw new Error("Failure in lookup('" + name + "')");
  652.     }
  653.   };
  654.  
  655.   /**
  656.    * Lookup a string in the GCLI string bundle
  657.    * @param name The name to lookup
  658.    * @param swaps An array of swaps. See stringBundle.formatStringFromName
  659.    * @return The looked up name
  660.    */
  661.   aMozl10n.lookupFormat = function(name, swaps) {
  662.     try {
  663.       return stringBundle.formatStringFromName(name, swaps, swaps.length);
  664.     }
  665.     catch (ex) {
  666.       throw new Error("Failure in lookupFormat('" + name + "')");
  667.     }
  668.   };
  669.  
  670. })(mozl10n);
  671.  
  672. define('gcli/index', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/types/node', 'gcli/cli', 'gcli/ui/inputter', 'gcli/ui/arg_fetch', 'gcli/ui/menu', 'gcli/ui/focus'], function(require, exports, module) {
  673.  
  674.   // The API for use by command authors
  675.   exports.addCommand = require('gcli/canon').addCommand;
  676.   exports.removeCommand = require('gcli/canon').removeCommand;
  677.   exports.lookup = mozl10n.lookup;
  678.   exports.lookupFormat = mozl10n.lookupFormat;
  679.  
  680.   // Internal startup process. Not exported
  681.   require('gcli/types/basic').startup();
  682.   require('gcli/types/javascript').startup();
  683.   require('gcli/types/node').startup();
  684.   require('gcli/cli').startup();
  685.  
  686.   var Requisition = require('gcli/cli').Requisition;
  687.   var cli = require('gcli/cli');
  688.   var Inputter = require('gcli/ui/inputter').Inputter;
  689.   var ArgFetcher = require('gcli/ui/arg_fetch').ArgFetcher;
  690.   var CommandMenu = require('gcli/ui/menu').CommandMenu;
  691.   var FocusManager = require('gcli/ui/focus').FocusManager;
  692.  
  693.   var jstype = require('gcli/types/javascript');
  694.   var nodetype = require('gcli/types/node');
  695.  
  696.   /**
  697.    * API for use by HUDService only.
  698.    * This code is internal and subject to change without notice.
  699.    */
  700.   exports._internal = {
  701.     require: require,
  702.     define: define,
  703.     console: console,
  704.  
  705.     /**
  706.      * createView() for Firefox requires an options object with the following
  707.      * members:
  708.      * - contentDocument: From the window of the attached tab
  709.      * - chromeDocument: GCLITerm.document
  710.      * - environment.hudId: GCLITerm.hudId
  711.      * - jsEnvironment.globalObject: 'window'
  712.      * - jsEnvironment.evalFunction: 'eval' in a sandbox
  713.      * - inputElement: GCLITerm.inputNode
  714.      * - completeElement: GCLITerm.completeNode
  715.      * - gcliTerm: GCLITerm
  716.      * - hintElement: GCLITerm.hintNode
  717.      * - inputBackgroundElement: GCLITerm.inputStack
  718.      */
  719.     createView: function(opts) {
  720.       opts.autoHide = true;
  721.       opts.requisition = new Requisition(opts.environment, opts.chromeDocument);
  722.       opts.completionPrompt = '';
  723.  
  724.       jstype.setGlobalObject(opts.jsEnvironment.globalObject);
  725.       nodetype.setDocument(opts.contentDocument);
  726.       cli.setEvalFunction(opts.jsEnvironment.evalFunction);
  727.  
  728.       // Create a FocusManager for the various parts to register with
  729.       if (!opts.focusManager) {
  730.         opts.debug = true;
  731.         opts.focusManager = new FocusManager({ document: opts.chromeDocument });
  732.       }
  733.  
  734.       opts.inputter = new Inputter(opts);
  735.       opts.inputter.update();
  736.       if (opts.gcliTerm) {
  737.         opts.focusManager.onFocus.add(opts.gcliTerm.show, opts.gcliTerm);
  738.         opts.focusManager.onBlur.add(opts.gcliTerm.hide, opts.gcliTerm);
  739.         opts.focusManager.addMonitoredElement(opts.gcliTerm.hintNode, 'gcliTerm');
  740.       }
  741.  
  742.       if (opts.hintElement) {
  743.         opts.menu = new CommandMenu(opts.chromeDocument, opts.requisition);
  744.         opts.hintElement.appendChild(opts.menu.element);
  745.  
  746.         opts.argFetcher = new ArgFetcher(opts.chromeDocument, opts.requisition);
  747.         opts.hintElement.appendChild(opts.argFetcher.element);
  748.  
  749.         opts.menu.onCommandChange();
  750.       }
  751.     },
  752.  
  753.     /**
  754.      * Undo the effects of createView() to prevent memory leaks
  755.      */
  756.     removeView: function(opts) {
  757.       opts.hintElement.removeChild(opts.menu.element);
  758.       opts.menu.destroy();
  759.       opts.hintElement.removeChild(opts.argFetcher.element);
  760.       opts.argFetcher.destroy();
  761.  
  762.       opts.inputter.destroy();
  763.       opts.focusManager.removeMonitoredElement(opts.gcliTerm.hintNode, 'gcliTerm');
  764.       opts.focusManager.onFocus.remove(opts.gcliTerm.show, opts.gcliTerm);
  765.       opts.focusManager.onBlur.remove(opts.gcliTerm.hide, opts.gcliTerm);
  766.       opts.focusManager.destroy();
  767.  
  768.       cli.unsetEvalFunction();
  769.       nodetype.unsetDocument();
  770.       jstype.unsetGlobalObject();
  771.  
  772.       opts.requisition.destroy();
  773.     },
  774.  
  775.     commandOutputManager: require('gcli/canon').commandOutputManager
  776.   };
  777. });
  778. /*
  779.  * Copyright 2009-2011 Mozilla Foundation and contributors
  780.  * Licensed under the New BSD license. See LICENSE.txt or:
  781.  * http://opensource.org/licenses/BSD-3-Clause
  782.  */
  783.  
  784. define('gcli/canon', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/types', 'gcli/types/basic'], function(require, exports, module) {
  785. var canon = exports;
  786.  
  787.  
  788. var util = require('gcli/util');
  789. var l10n = require('gcli/l10n');
  790.  
  791. var types = require('gcli/types');
  792. var Status = require('gcli/types').Status;
  793. var BooleanType = require('gcli/types/basic').BooleanType;
  794.  
  795.  
  796. /**
  797.  * A lookup hash of our registered commands
  798.  */
  799. var commands = {};
  800.  
  801. /**
  802.  * A sorted list of command names, we regularly want them in order, so pre-sort
  803.  */
  804. var commandNames = [];
  805.  
  806. /**
  807.  * Implement the localization algorithm for any documentation objects (i.e.
  808.  * description and manual) in a command.
  809.  * @param data The data assigned to a description or manual property
  810.  * @param onUndefined If data == null, should we return the data untouched or
  811.  * lookup a 'we don't know' key in it's place.
  812.  */
  813. function lookup(data, onUndefined) {
  814.   if (data == null) {
  815.     if (onUndefined) {
  816.       return l10n.lookup(onUndefined);
  817.     }
  818.  
  819.     return data;
  820.   }
  821.  
  822.   if (typeof data === 'string') {
  823.     return data;
  824.   }
  825.  
  826.   if (typeof data === 'object') {
  827.     if (data.key) {
  828.       return l10n.lookup(data.key);
  829.     }
  830.  
  831.     var locales = l10n.getPreferredLocales();
  832.     var translated;
  833.     locales.some(function(locale) {
  834.       translated = data[locale];
  835.       return translated != null;
  836.     });
  837.     if (translated != null) {
  838.       return translated;
  839.     }
  840.  
  841.     console.error('Can\'t find locale in descriptions: ' +
  842.             'locales=' + JSON.stringify(locales) + ', ' +
  843.             'description=' + JSON.stringify(data));
  844.     return '(No description)';
  845.   }
  846.  
  847.   return l10n.lookup(onUndefined);
  848. }
  849.  
  850. /**
  851.  * The command object is mostly just setup around a commandSpec (as passed to
  852.  * #addCommand()).
  853.  */
  854. function Command(commandSpec) {
  855.   Object.keys(commandSpec).forEach(function(key) {
  856.     this[key] = commandSpec[key];
  857.   }, this);
  858.  
  859.   if (!this.name) {
  860.     throw new Error('All registered commands must have a name');
  861.   }
  862.  
  863.   if (this.params == null) {
  864.     this.params = [];
  865.   }
  866.   if (!Array.isArray(this.params)) {
  867.     throw new Error('command.params must be an array in ' + this.name);
  868.   }
  869.  
  870.   this.description = 'description' in this ? this.description : undefined;
  871.   this.description = lookup(this.description, 'canonDescNone');
  872.   this.manual = 'manual' in this ? this.manual : undefined;
  873.   this.manual = lookup(this.manual);
  874.  
  875.   // At this point this.params has nested param groups. We want to flatten it
  876.   // out and replace the param object literals with Parameter objects
  877.   var paramSpecs = this.params;
  878.   this.params = [];
  879.  
  880.   // Track if the user is trying to mix default params and param groups.
  881.   // All the non-grouped parameters must come before all the param groups
  882.   // because non-grouped parameters can be assigned positionally, so their
  883.   // index is important. We don't want 'holes' in the order caused by
  884.   // parameter groups.
  885.   var usingGroups = false;
  886.  
  887.   // In theory this could easily be made recursive, so param groups could
  888.   // contain nested param groups. Current thinking is that the added
  889.   // complexity for the UI probably isn't worth it, so this implementation
  890.   // prevents nesting.
  891.   paramSpecs.forEach(function(spec) {
  892.     if (!spec.group) {
  893.       if (usingGroups) {
  894.         console.error('Parameters can\'t come after param groups.' +
  895.             ' Ignoring ' + this.name + '/' + spec.name);
  896.       }
  897.       else {
  898.         var param = new Parameter(spec, this, null);
  899.         this.params.push(param);
  900.       }
  901.     }
  902.     else {
  903.       spec.params.forEach(function(ispec) {
  904.         var param = new Parameter(ispec, this, spec.group);
  905.         this.params.push(param);
  906.       }, this);
  907.  
  908.       usingGroups = true;
  909.     }
  910.   }, this);
  911. }
  912.  
  913. canon.Command = Command;
  914.  
  915.  
  916. /**
  917.  * A wrapper for a paramSpec so we can sort out shortened versions names for
  918.  * option switches
  919.  */
  920. function Parameter(paramSpec, command, groupName) {
  921.   this.command = command || { name: 'unnamed' };
  922.  
  923.   Object.keys(paramSpec).forEach(function(key) {
  924.     this[key] = paramSpec[key];
  925.   }, this);
  926.  
  927.   this.description = 'description' in this ? this.description : undefined;
  928.   this.description = lookup(this.description, 'canonDescNone');
  929.   this.manual = 'manual' in this ? this.manual : undefined;
  930.   this.manual = lookup(this.manual);
  931.   this.groupName = groupName;
  932.  
  933.   if (!this.name) {
  934.     throw new Error('In ' + this.command.name +
  935.       ': all params must have a name');
  936.   }
  937.  
  938.   var typeSpec = this.type;
  939.   this.type = types.getType(typeSpec);
  940.   if (this.type == null) {
  941.     console.error('Known types: ' + types.getTypeNames().join(', '));
  942.     throw new Error('In ' + this.command.name + '/' + this.name +
  943.       ': can\'t find type for: ' + JSON.stringify(typeSpec));
  944.   }
  945.  
  946.   // boolean parameters have an implicit defaultValue:false, which should
  947.   // not be changed. See the docs.
  948.   if (this.type instanceof BooleanType) {
  949.     if ('defaultValue' in this) {
  950.       console.error('In ' + this.command.name + '/' + this.name +
  951.           ': boolean parameters can not have a defaultValue.' +
  952.           ' Ignoring');
  953.     }
  954.     this.defaultValue = false;
  955.   }
  956.  
  957.   // Check the defaultValue for validity.
  958.   // Both undefined and null get a pass on this test. undefined is used when
  959.   // there is no defaultValue, and null is used when the parameter is
  960.   // optional, neither are required to parse and stringify.
  961.   if (this.defaultValue != null) {
  962.     try {
  963.       var defaultText = this.type.stringify(this.defaultValue);
  964.       var defaultConversion = this.type.parseString(defaultText);
  965.       if (defaultConversion.getStatus() !== Status.VALID) {
  966.         console.error('In ' + this.command.name + '/' + this.name +
  967.             ': Error round tripping defaultValue. status = ' +
  968.             defaultConversion.getStatus());
  969.       }
  970.     }
  971.     catch (ex) {
  972.       console.error('In ' + this.command.name + '/' + this.name +
  973.         ': ' + ex);
  974.     }
  975.   }
  976. }
  977.  
  978. /**
  979.  * Does the given name uniquely identify this param (among the other params
  980.  * in this command)
  981.  * @param name The name to check
  982.  */
  983. Parameter.prototype.isKnownAs = function(name) {
  984.   if (name === '--' + this.name) {
  985.     return true;
  986.   }
  987.   return false;
  988. };
  989.  
  990. /**
  991.  * Is the user required to enter data for this parameter? (i.e. has
  992.  * defaultValue been set to something other than undefined)
  993.  */
  994. Parameter.prototype.isDataRequired = function() {
  995.   return this.defaultValue === undefined;
  996. };
  997.  
  998. /**
  999.  * Are we allowed to assign data to this parameter using positional
  1000.  * parameters?
  1001.  */
  1002. Parameter.prototype.isPositionalAllowed = function() {
  1003.   return this.groupName == null;
  1004. };
  1005.  
  1006. canon.Parameter = Parameter;
  1007.  
  1008.  
  1009. /**
  1010.  * Add a command to the canon of known commands.
  1011.  * This function is exposed to the outside world (via gcli/index). It is
  1012.  * documented in docs/index.md for all the world to see.
  1013.  * @param commandSpec The command and its metadata.
  1014.  * @return The new command
  1015.  */
  1016. canon.addCommand = function addCommand(commandSpec) {
  1017.   var command = new Command(commandSpec);
  1018.   commands[commandSpec.name] = command;
  1019.   commandNames.push(commandSpec.name);
  1020.   commandNames.sort();
  1021.  
  1022.   canon.canonChange();
  1023.   return command;
  1024. };
  1025.  
  1026. /**
  1027.  * Remove an individual command. The opposite of #addCommand().
  1028.  * @param commandOrName Either a command name or the command itself.
  1029.  */
  1030. canon.removeCommand = function removeCommand(commandOrName) {
  1031.   var name = typeof commandOrName === 'string' ?
  1032.           commandOrName :
  1033.           commandOrName.name;
  1034.   delete commands[name];
  1035.   commandNames = commandNames.filter(function(test) {
  1036.     return test !== name;
  1037.   });
  1038.  
  1039.   canon.canonChange();
  1040. };
  1041.  
  1042. /**
  1043.  * Retrieve a command by name
  1044.  * @param name The name of the command to retrieve
  1045.  */
  1046. canon.getCommand = function getCommand(name) {
  1047.   return commands[name];
  1048. };
  1049.  
  1050. /**
  1051.  * Get an array of all the registered commands.
  1052.  */
  1053. canon.getCommands = function getCommands() {
  1054.   // return Object.values(commands);
  1055.   return Object.keys(commands).map(function(name) {
  1056.     return commands[name];
  1057.   }, this);
  1058. };
  1059.  
  1060. /**
  1061.  * Get an array containing the names of the registered commands.
  1062.  */
  1063. canon.getCommandNames = function getCommandNames() {
  1064.   return commandNames.slice(0);
  1065. };
  1066.  
  1067. /**
  1068.  * Enable people to be notified of changes to the list of commands
  1069.  */
  1070. canon.canonChange = util.createEvent('canon.canonChange');
  1071.  
  1072. /**
  1073.  * CommandOutputManager stores the output objects generated by executed
  1074.  * commands.
  1075.  *
  1076.  * CommandOutputManager is exposed (via canon.commandOutputManager) to the the
  1077.  * outside world and could (but shouldn't) be used before gcli.startup() has
  1078.  * been called. This could should be defensive to that where possible, and we
  1079.  * should certainly document if the use of it or similar will fail if used too
  1080.  * soon.
  1081.  */
  1082. function CommandOutputManager() {
  1083.   this._event = util.createEvent('CommandOutputManager');
  1084. }
  1085.  
  1086. /**
  1087.  * Call this method to notify the manager (and therefore all listeners) of a
  1088.  * new or updated command output.
  1089.  * @param output The command output object that has been created or updated.
  1090.  */
  1091. CommandOutputManager.prototype.sendCommandOutput = function(output) {
  1092.   this._event({ output: output });
  1093. };
  1094.  
  1095. /**
  1096.  * Register a function to be called whenever there is a new command output
  1097.  * object.
  1098.  */
  1099. CommandOutputManager.prototype.addListener = function(fn, ctx) {
  1100.   this._event.add(fn, ctx);
  1101. };
  1102.  
  1103. /**
  1104.  * Undo the effects of CommandOutputManager.addListener()
  1105.  */
  1106. CommandOutputManager.prototype.removeListener = function(fn, ctx) {
  1107.   this._event.remove(fn, ctx);
  1108. };
  1109.  
  1110. canon.CommandOutputManager = CommandOutputManager;
  1111.  
  1112. /**
  1113.  * We maintain a global command output manager for the majority case where
  1114.  * there is only one important set of outputs.
  1115.  */
  1116. canon.commandOutputManager = new CommandOutputManager();
  1117.  
  1118.  
  1119. });
  1120. /*
  1121.  * Copyright 2009-2011 Mozilla Foundation and contributors
  1122.  * Licensed under the New BSD license. See LICENSE.txt or:
  1123.  * http://opensource.org/licenses/BSD-3-Clause
  1124.  */
  1125.  
  1126. define('gcli/util', ['require', 'exports', 'module' ], function(require, exports, module) {
  1127.  
  1128. /*
  1129.  * This module is a Pilot-Lite. It exports a number of objects that replicate
  1130.  * parts of the Pilot project. It aims to be mostly API compatible, while
  1131.  * removing the submodule complexity and helping us make things work inside
  1132.  * Firefox.
  1133.  * The Pilot compatible exports are: console/dom/event
  1134.  *
  1135.  * In addition it contains a small event library similar to EventEmitter but
  1136.  * which makes it harder to mistake the event in use.
  1137.  */
  1138.  
  1139.  
  1140. //------------------------------------------------------------------------------
  1141.  
  1142. /**
  1143.  * Create an event.
  1144.  * For use as follows:
  1145.  *
  1146.  *   function Hat() {
  1147.  *     this.putOn = createEvent();
  1148.  *     ...
  1149.  *   }
  1150.  *   Hat.prototype.adorn = function(person) {
  1151.  *     this.putOn({ hat: hat, person: person });
  1152.  *     ...
  1153.  *   }
  1154.  *
  1155.  *   var hat = new Hat();
  1156.  *   hat.putOn.add(function(ev) {
  1157.  *     console.log('The hat ', ev.hat, ' has is worn by ', ev.person);
  1158.  *   }, scope);
  1159.  *
  1160.  * @param name Optional name to help with debugging
  1161.  */
  1162. exports.createEvent = function(name) {
  1163.   var handlers = [];
  1164.  
  1165.   /**
  1166.    * This is how the event is triggered.
  1167.    * @param ev The event object to be passed to the event listeners
  1168.    */
  1169.   var event = function(ev) {
  1170.     // Use for rather than forEach because it step debugs better, which is
  1171.     // important for debugging events
  1172.     for (var i = 0; i < handlers.length; i++) {
  1173.       var handler = handlers[i];
  1174.       handler.func.call(handler.scope, ev);
  1175.     }
  1176.   };
  1177.  
  1178.   /**
  1179.    * Add a new handler function
  1180.    * @param func The function to call when this event is triggered
  1181.    * @param scope Optional 'this' object for the function call
  1182.    */
  1183.   event.add = function(func, scope) {
  1184.     handlers.push({ func: func, scope: scope });
  1185.   };
  1186.  
  1187.   /**
  1188.    * Remove a handler function added through add(). Both func and scope must
  1189.    * be strict equals (===) the values used in the call to add()
  1190.    * @param func The function to call when this event is triggered
  1191.    * @param scope Optional 'this' object for the function call
  1192.    */
  1193.   event.remove = function(func, scope) {
  1194.     handlers = handlers.filter(function(test) {
  1195.       return test.func !== func && test.scope !== scope;
  1196.     });
  1197.   };
  1198.  
  1199.   /**
  1200.    * Remove all handlers.
  1201.    * Reset the state of this event back to it's post create state
  1202.    */
  1203.   event.removeAll = function() {
  1204.     handlers = [];
  1205.   };
  1206.  
  1207.   return event;
  1208. };
  1209.  
  1210.  
  1211. //------------------------------------------------------------------------------
  1212.  
  1213. var dom = {};
  1214.  
  1215. var NS_XHTML = 'http://www.w3.org/1999/xhtml';
  1216.  
  1217. /**
  1218.  * Pass-through to createElement or createElementNS
  1219.  * @param doc The document in which to create the element
  1220.  * @param tag The name of the tag to create
  1221.  * @param ns Custom namespace, HTML/XHTML is assumed if this is missing
  1222.  * @returns The created element
  1223.  */
  1224. dom.createElement = function(doc, tag, ns) {
  1225.   // If we've not been given a namespace, but the document is XML, then we
  1226.   // use an XHTML namespace, otherwise we use HTML
  1227.   if (ns == null && doc.xmlVersion != null) {
  1228.     ns = NS_XHTML;
  1229.   }
  1230.   if (ns == null) {
  1231.     return doc.createElement(tag);
  1232.   }
  1233.   else {
  1234.     return doc.createElementNS(ns, tag);
  1235.   }
  1236. };
  1237.  
  1238. /**
  1239.  * Remove all the child nodes from this node
  1240.  * @param elem The element that should have it's children removed
  1241.  */
  1242. dom.clearElement = function(elem) {
  1243.   while (elem.hasChildNodes()) {
  1244.     elem.removeChild(elem.firstChild);
  1245.   }
  1246. };
  1247.  
  1248. /**
  1249.  * Create a style element in the document head, and add the given CSS text to
  1250.  * it.
  1251.  * @param cssText The CSS declarations to append
  1252.  * @param doc The document element to work from
  1253.  */
  1254. dom.importCss = function(cssText, doc) {
  1255.   doc = doc || document;
  1256.  
  1257.   var style = dom.createElement(doc, 'style');
  1258.   style.appendChild(doc.createTextNode(cssText));
  1259.  
  1260.   var head = doc.getElementsByTagName('head')[0] || doc.documentElement;
  1261.   head.appendChild(style);
  1262.  
  1263.   return style;
  1264. };
  1265.  
  1266. /**
  1267.  * Using setInnerHtml(foo) rather than innerHTML = foo allows us to enable
  1268.  * tweaks in XHTML documents.
  1269.  */
  1270. dom.setInnerHtml = function(elem, html) {
  1271.   if (!this.document || elem.namespaceURI === NS_XHTML) {
  1272.     try {
  1273.       dom.clearElement(elem);
  1274.       var range = elem.ownerDocument.createRange();
  1275.       html = '<div xmlns="' + NS_XHTML + '">' + html + '</div>';
  1276.       elem.appendChild(range.createContextualFragment(html));
  1277.     }
  1278.     catch (ex) {
  1279.       elem.innerHTML = html;
  1280.     }
  1281.   }
  1282.   else {
  1283.     elem.innerHTML = html;
  1284.   }
  1285. };
  1286.  
  1287. exports.dom = dom;
  1288.  
  1289.  
  1290. //------------------------------------------------------------------------------
  1291.  
  1292. /**
  1293.  * Various event utilities
  1294.  */
  1295. var event = {};
  1296.  
  1297. /**
  1298.  * Keyboard handling is a mess. http://unixpapa.com/js/key.html
  1299.  * It would be good to use DOM L3 Keyboard events,
  1300.  * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
  1301.  * however only Webkit supports them, and there isn't a shim on Monernizr:
  1302.  * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
  1303.  * and when the code that uses this KeyEvent was written, nothing was clear,
  1304.  * so instead, we're using this unmodern shim:
  1305.  * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
  1306.  * See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
  1307.  * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
  1308.  */
  1309. if ('KeyEvent' in this) {
  1310.   event.KeyEvent = this.KeyEvent;
  1311. }
  1312. else {
  1313.   event.KeyEvent = {
  1314.     DOM_VK_CANCEL: 3,
  1315.     DOM_VK_HELP: 6,
  1316.     DOM_VK_BACK_SPACE: 8,
  1317.     DOM_VK_TAB: 9,
  1318.     DOM_VK_CLEAR: 12,
  1319.     DOM_VK_RETURN: 13,
  1320.     DOM_VK_ENTER: 14,
  1321.     DOM_VK_SHIFT: 16,
  1322.     DOM_VK_CONTROL: 17,
  1323.     DOM_VK_ALT: 18,
  1324.     DOM_VK_PAUSE: 19,
  1325.     DOM_VK_CAPS_LOCK: 20,
  1326.     DOM_VK_ESCAPE: 27,
  1327.     DOM_VK_SPACE: 32,
  1328.     DOM_VK_PAGE_UP: 33,
  1329.     DOM_VK_PAGE_DOWN: 34,
  1330.     DOM_VK_END: 35,
  1331.     DOM_VK_HOME: 36,
  1332.     DOM_VK_LEFT: 37,
  1333.     DOM_VK_UP: 38,
  1334.     DOM_VK_RIGHT: 39,
  1335.     DOM_VK_DOWN: 40,
  1336.     DOM_VK_PRINTSCREEN: 44,
  1337.     DOM_VK_INSERT: 45,
  1338.     DOM_VK_DELETE: 46,
  1339.     DOM_VK_0: 48,
  1340.     DOM_VK_1: 49,
  1341.     DOM_VK_2: 50,
  1342.     DOM_VK_3: 51,
  1343.     DOM_VK_4: 52,
  1344.     DOM_VK_5: 53,
  1345.     DOM_VK_6: 54,
  1346.     DOM_VK_7: 55,
  1347.     DOM_VK_8: 56,
  1348.     DOM_VK_9: 57,
  1349.     DOM_VK_SEMICOLON: 59,
  1350.     DOM_VK_EQUALS: 61,
  1351.     DOM_VK_A: 65,
  1352.     DOM_VK_B: 66,
  1353.     DOM_VK_C: 67,
  1354.     DOM_VK_D: 68,
  1355.     DOM_VK_E: 69,
  1356.     DOM_VK_F: 70,
  1357.     DOM_VK_G: 71,
  1358.     DOM_VK_H: 72,
  1359.     DOM_VK_I: 73,
  1360.     DOM_VK_J: 74,
  1361.     DOM_VK_K: 75,
  1362.     DOM_VK_L: 76,
  1363.     DOM_VK_M: 77,
  1364.     DOM_VK_N: 78,
  1365.     DOM_VK_O: 79,
  1366.     DOM_VK_P: 80,
  1367.     DOM_VK_Q: 81,
  1368.     DOM_VK_R: 82,
  1369.     DOM_VK_S: 83,
  1370.     DOM_VK_T: 84,
  1371.     DOM_VK_U: 85,
  1372.     DOM_VK_V: 86,
  1373.     DOM_VK_W: 87,
  1374.     DOM_VK_X: 88,
  1375.     DOM_VK_Y: 89,
  1376.     DOM_VK_Z: 90,
  1377.     DOM_VK_CONTEXT_MENU: 93,
  1378.     DOM_VK_NUMPAD0: 96,
  1379.     DOM_VK_NUMPAD1: 97,
  1380.     DOM_VK_NUMPAD2: 98,
  1381.     DOM_VK_NUMPAD3: 99,
  1382.     DOM_VK_NUMPAD4: 100,
  1383.     DOM_VK_NUMPAD5: 101,
  1384.     DOM_VK_NUMPAD6: 102,
  1385.     DOM_VK_NUMPAD7: 103,
  1386.     DOM_VK_NUMPAD8: 104,
  1387.     DOM_VK_NUMPAD9: 105,
  1388.     DOM_VK_MULTIPLY: 106,
  1389.     DOM_VK_ADD: 107,
  1390.     DOM_VK_SEPARATOR: 108,
  1391.     DOM_VK_SUBTRACT: 109,
  1392.     DOM_VK_DECIMAL: 110,
  1393.     DOM_VK_DIVIDE: 111,
  1394.     DOM_VK_F1: 112,
  1395.     DOM_VK_F2: 113,
  1396.     DOM_VK_F3: 114,
  1397.     DOM_VK_F4: 115,
  1398.     DOM_VK_F5: 116,
  1399.     DOM_VK_F6: 117,
  1400.     DOM_VK_F7: 118,
  1401.     DOM_VK_F8: 119,
  1402.     DOM_VK_F9: 120,
  1403.     DOM_VK_F10: 121,
  1404.     DOM_VK_F11: 122,
  1405.     DOM_VK_F12: 123,
  1406.     DOM_VK_F13: 124,
  1407.     DOM_VK_F14: 125,
  1408.     DOM_VK_F15: 126,
  1409.     DOM_VK_F16: 127,
  1410.     DOM_VK_F17: 128,
  1411.     DOM_VK_F18: 129,
  1412.     DOM_VK_F19: 130,
  1413.     DOM_VK_F20: 131,
  1414.     DOM_VK_F21: 132,
  1415.     DOM_VK_F22: 133,
  1416.     DOM_VK_F23: 134,
  1417.     DOM_VK_F24: 135,
  1418.     DOM_VK_NUM_LOCK: 144,
  1419.     DOM_VK_SCROLL_LOCK: 145,
  1420.     DOM_VK_COMMA: 188,
  1421.     DOM_VK_PERIOD: 190,
  1422.     DOM_VK_SLASH: 191,
  1423.     DOM_VK_BACK_QUOTE: 192,
  1424.     DOM_VK_OPEN_BRACKET: 219,
  1425.     DOM_VK_BACK_SLASH: 220,
  1426.     DOM_VK_CLOSE_BRACKET: 221,
  1427.     DOM_VK_QUOTE: 222,
  1428.     DOM_VK_META: 224
  1429.   };
  1430. }
  1431.  
  1432. exports.event = event;
  1433.  
  1434.  
  1435. });
  1436. /*
  1437.  * Copyright 2009-2011 Mozilla Foundation and contributors
  1438.  * Licensed under the New BSD license. See LICENSE.txt or:
  1439.  * http://opensource.org/licenses/BSD-3-Clause
  1440.  */
  1441.  
  1442. define('gcli/l10n', ['require', 'exports', 'module' ], function(require, exports, module) {
  1443.  
  1444. Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
  1445. Components.utils.import('resource://gre/modules/Services.jsm');
  1446.  
  1447. XPCOMUtils.defineLazyGetter(this, 'stringBundle', function () {
  1448.   return Services.strings.createBundle('chrome://browser/locale/devtools/gcli.properties');
  1449. });
  1450.  
  1451. /*
  1452.  * Not supported when embedded - we're doing things the Mozilla way not the
  1453.  * require.js way.
  1454.  */
  1455. exports.registerStringsSource = function(modulePath) {
  1456.   throw new Error('registerStringsSource is not available in mozilla');
  1457. };
  1458.  
  1459. exports.unregisterStringsSource = function(modulePath) {
  1460.   throw new Error('unregisterStringsSource is not available in mozilla');
  1461. };
  1462.  
  1463. exports.lookupSwap = function(key, swaps) {
  1464.   throw new Error('lookupSwap is not available in mozilla');
  1465. };
  1466.  
  1467. exports.lookupPlural = function(key, ord, swaps) {
  1468.   throw new Error('lookupPlural is not available in mozilla');
  1469. };
  1470.  
  1471. exports.getPreferredLocales = function() {
  1472.   return [ 'root' ];
  1473. };
  1474.  
  1475. /** @see lookup() in lib/gcli/l10n.js */
  1476. exports.lookup = function(key) {
  1477.   try {
  1478.     return stringBundle.GetStringFromName(key);
  1479.   }
  1480.   catch (ex) {
  1481.     console.error('Failed to lookup ', key, ex);
  1482.     return key;
  1483.   }
  1484. };
  1485.  
  1486. /** @see lookupFormat in lib/gcli/l10n.js */
  1487. exports.lookupFormat = function(key, swaps) {
  1488.   try {
  1489.     return stringBundle.formatStringFromName(key, swaps, swaps.length);
  1490.   }
  1491.   catch (ex) {
  1492.     console.error('Failed to format ', key, ex);
  1493.     return key;
  1494.   }
  1495. };
  1496.  
  1497.  
  1498. });
  1499. /*
  1500.  * Copyright 2009-2011 Mozilla Foundation and contributors
  1501.  * Licensed under the New BSD license. See LICENSE.txt or:
  1502.  * http://opensource.org/licenses/BSD-3-Clause
  1503.  */
  1504.  
  1505. define('gcli/types', ['require', 'exports', 'module' , 'gcli/argument'], function(require, exports, module) {
  1506. var types = exports;
  1507.  
  1508.  
  1509. var Argument = require('gcli/argument').Argument;
  1510.  
  1511.  
  1512. /**
  1513.  * Some types can detect validity, that is to say they can distinguish between
  1514.  * valid and invalid values.
  1515.  * We might want to change these constants to be numbers for better performance
  1516.  */
  1517. var Status = {
  1518.   /**
  1519.    * The conversion process worked without any problem, and the value is
  1520.    * valid. There are a number of failure states, so the best way to check
  1521.    * for failure is (x !== Status.VALID)
  1522.    */
  1523.   VALID: {
  1524.     toString: function() { return 'VALID'; },
  1525.     valueOf: function() { return 0; }
  1526.   },
  1527.  
  1528.   /**
  1529.    * A conversion process failed, however it was noted that the string
  1530.    * provided to 'parse()' could be VALID by the addition of more characters,
  1531.    * so the typing may not be actually incorrect yet, just unfinished.
  1532.    * @see Status.ERROR
  1533.    */
  1534.   INCOMPLETE: {
  1535.     toString: function() { return 'INCOMPLETE'; },
  1536.     valueOf: function() { return 1; }
  1537.   },
  1538.  
  1539.   /**
  1540.    * The conversion process did not work, the value should be null and a
  1541.    * reason for failure should have been provided. In addition some
  1542.    * completion values may be available.
  1543.    * @see Status.INCOMPLETE
  1544.    */
  1545.   ERROR: {
  1546.     toString: function() { return 'ERROR'; },
  1547.     valueOf: function() { return 2; }
  1548.   },
  1549.  
  1550.   /**
  1551.    * A combined status is the worser of the provided statuses. The statuses
  1552.    * can be provided either as a set of arguments or a single array
  1553.    */
  1554.   combine: function() {
  1555.     var combined = Status.VALID;
  1556.     for (var i = 0; i < arguments.length; i++) {
  1557.       var status = arguments[i];
  1558.       if (Array.isArray(status)) {
  1559.         status = Status.combine.apply(null, status);
  1560.       }
  1561.       if (status > combined) {
  1562.         combined = status;
  1563.       }
  1564.     }
  1565.     return combined;
  1566.   }
  1567. };
  1568. types.Status = Status;
  1569.  
  1570. /**
  1571.  * The type.parse() method converts an Argument into a value, Conversion is
  1572.  * a wrapper to that value.
  1573.  * Conversion is needed to collect a number of properties related to that
  1574.  * conversion in one place, i.e. to handle errors and provide traceability.
  1575.  * @param value The result of the conversion
  1576.  * @param arg The data from which the conversion was made
  1577.  * @param status See the Status values [VALID|INCOMPLETE|ERROR] defined above.
  1578.  * The default status is Status.VALID.
  1579.  * @param message If status=ERROR, there should be a message to describe the
  1580.  * error. A message is not needed unless for other statuses, but could be
  1581.  * present for any status including VALID (in the case where we want to note a
  1582.  * warning, for example).
  1583.  * See BUG 664676: GCLI conversion error messages should be localized
  1584.  * @param predictions If status=INCOMPLETE, there could be predictions as to
  1585.  * the options available to complete the input.
  1586.  * We generally expect there to be about 7 predictions (to match human list
  1587.  * comprehension ability) however it is valid to provide up to about 20,
  1588.  * or less. It is the job of the predictor to decide a smart cut-off.
  1589.  * For example if there are 4 very good matches and 4 very poor ones,
  1590.  * probably only the 4 very good matches should be presented.
  1591.  * The predictions are presented either as an array of prediction objects or as
  1592.  * a function which returns this array when called with no parameters.
  1593.  * Each prediction object has the following shape:
  1594.  *     {
  1595.  *       name: '...',     // textual completion. i.e. what the cli uses
  1596.  *       value: { ... },  // value behind the textual completion
  1597.  *       incomplete: true // this completion is only partial (optional)
  1598.  *     }
  1599.  * The 'incomplete' property could be used to denote a valid completion which
  1600.  * could have sub-values (e.g. for tree navigation).
  1601.  */
  1602. function Conversion(value, arg, status, message, predictions) {
  1603.   // The result of the conversion process. Will be null if status != VALID
  1604.   this.value = value;
  1605.  
  1606.   // Allow us to trace where this Conversion came from
  1607.   this.arg = arg;
  1608.   if (arg == null) {
  1609.     throw new Error('Missing arg');
  1610.   }
  1611.  
  1612.   this._status = status || Status.VALID;
  1613.   this.message = message;
  1614.   this.predictions = predictions;
  1615. }
  1616.  
  1617. types.Conversion = Conversion;
  1618.  
  1619. /**
  1620.  * Ensure that all arguments that are part of this conversion know what they
  1621.  * are assigned to.
  1622.  * @param assignment The Assignment (param/conversion link) to inform the
  1623.  * argument about.
  1624.  */
  1625. Conversion.prototype.assign = function(assignment) {
  1626.   this.arg.assign(assignment);
  1627. };
  1628.  
  1629. /**
  1630.  * Work out if there is information provided in the contained argument.
  1631.  */
  1632. Conversion.prototype.isDataProvided = function() {
  1633.   var argProvided = this.arg.text !== '';
  1634.   return this.value !== undefined || argProvided;
  1635. };
  1636.  
  1637. /**
  1638.  * 2 conversions are equal if and only if their args are equal (argEquals) and
  1639.  * their values are equal (valueEquals).
  1640.  * @param that The conversion object to compare against.
  1641.  */
  1642. Conversion.prototype.equals = function(that) {
  1643.   if (this === that) {
  1644.     return true;
  1645.   }
  1646.   if (that == null) {
  1647.     return false;
  1648.   }
  1649.   return this.valueEquals(that) && this.argEquals(that);
  1650. };
  1651.  
  1652. /**
  1653.  * Check that the value in this conversion is strict equal to the value in the
  1654.  * provided conversion.
  1655.  * @param that The conversion to compare values with
  1656.  */
  1657. Conversion.prototype.valueEquals = function(that) {
  1658.   return this.value === that.value;
  1659. };
  1660.  
  1661. /**
  1662.  * Check that the argument in this conversion is equal to the value in the
  1663.  * provided conversion as defined by the argument (i.e. arg.equals).
  1664.  * @param that The conversion to compare arguments with
  1665.  */
  1666. Conversion.prototype.argEquals = function(that) {
  1667.   return this.arg.equals(that.arg);
  1668. };
  1669.  
  1670. /**
  1671.  * Accessor for the status of this conversion
  1672.  */
  1673. Conversion.prototype.getStatus = function(arg) {
  1674.   return this._status;
  1675. };
  1676.  
  1677. /**
  1678.  * Defined by the toString() value provided by the argument
  1679.  */
  1680. Conversion.prototype.toString = function() {
  1681.   return this.arg.toString();
  1682. };
  1683.  
  1684. /**
  1685.  * If status === INCOMPLETE, then we may be able to provide predictions as to
  1686.  * how the argument can be completed.
  1687.  * @return An array of items, where each item is an object with the following
  1688.  * properties:
  1689.  * - name (mandatory): Displayed to the user, and typed in. No whitespace
  1690.  * - description (optional): Short string for display in a tool-tip
  1691.  * - manual (optional): Longer description which details usage
  1692.  * - incomplete (optional): Indicates that the prediction if used should not
  1693.  *   be considered necessarily sufficient, which typically will mean that the
  1694.  *   UI should not append a space to the completion
  1695.  * - value (optional): If a value property is present, this will be used as the
  1696.  *   value of the conversion, otherwise the item itself will be used.
  1697.  */
  1698. Conversion.prototype.getPredictions = function() {
  1699.   if (typeof this.predictions === 'function') {
  1700.     return this.predictions();
  1701.   }
  1702.   return this.predictions || [];
  1703. };
  1704.  
  1705. /**
  1706.  * ArrayConversion is a special Conversion, needed because arrays are converted
  1707.  * member by member rather then as a whole, which means we can track the
  1708.  * conversion if individual array elements. So an ArrayConversion acts like a
  1709.  * normal Conversion (which is needed as Assignment requires a Conversion) but
  1710.  * it can also be devolved into a set of Conversions for each array member.
  1711.  */
  1712. function ArrayConversion(conversions, arg) {
  1713.   this.arg = arg;
  1714.   this.conversions = conversions;
  1715.   this.value = conversions.map(function(conversion) {
  1716.     return conversion.value;
  1717.   }, this);
  1718.  
  1719.   this._status = Status.combine(conversions.map(function(conversion) {
  1720.     return conversion.getStatus();
  1721.   }));
  1722.  
  1723.   // This message is just for reporting errors like "not enough values"
  1724.   // rather that for problems with individual values.
  1725.   this.message = '';
  1726.  
  1727.   // Predictions are generally provided by individual values
  1728.   this.predictions = [];
  1729. }
  1730.  
  1731. ArrayConversion.prototype = Object.create(Conversion.prototype);
  1732.  
  1733. ArrayConversion.prototype.assign = function(assignment) {
  1734.   this.conversions.forEach(function(conversion) {
  1735.     conversion.assign(assignment);
  1736.   }, this);
  1737.   this.assignment = assignment;
  1738. };
  1739.  
  1740. ArrayConversion.prototype.getStatus = function(arg) {
  1741.   if (arg && arg.conversion) {
  1742.     return arg.conversion.getStatus();
  1743.   }
  1744.   return this._status;
  1745. };
  1746.  
  1747. ArrayConversion.prototype.isDataProvided = function() {
  1748.   return this.conversions.length > 0;
  1749. };
  1750.  
  1751. ArrayConversion.prototype.valueEquals = function(that) {
  1752.   if (!(that instanceof ArrayConversion)) {
  1753.     throw new Error('Can\'t compare values with non ArrayConversion');
  1754.   }
  1755.  
  1756.   if (this.value === that.value) {
  1757.     return true;
  1758.   }
  1759.  
  1760.   if (this.value.length !== that.value.length) {
  1761.     return false;
  1762.   }
  1763.  
  1764.   for (var i = 0; i < this.conversions.length; i++) {
  1765.     if (!this.conversions[i].valueEquals(that.conversions[i])) {
  1766.       return false;
  1767.     }
  1768.   }
  1769.  
  1770.   return true;
  1771. };
  1772.  
  1773. ArrayConversion.prototype.toString = function() {
  1774.   return '[ ' + this.conversions.map(function(conversion) {
  1775.     return conversion.toString();
  1776.   }, this).join(', ') + ' ]';
  1777. };
  1778.  
  1779. types.ArrayConversion = ArrayConversion;
  1780.  
  1781.  
  1782. /**
  1783.  * Most of our types are 'static' e.g. there is only one type of 'string',
  1784.  * however some types like 'selection' and 'deferred' are customizable.
  1785.  * The basic Type type isn't useful, but does provide documentation about what
  1786.  * types do.
  1787.  */
  1788. function Type() {
  1789. }
  1790.  
  1791. /**
  1792.  * Convert the given <tt>value</tt> to a string representation.
  1793.  * Where possible, there should be round-tripping between values and their
  1794.  * string representations.
  1795.  */
  1796. Type.prototype.stringify = function(value) {
  1797.   throw new Error('Not implemented');
  1798. };
  1799.  
  1800. /**
  1801.  * Convert the given <tt>arg</tt> to an instance of this type.
  1802.  * Where possible, there should be round-tripping between values and their
  1803.  * string representations.
  1804.  * @param arg An instance of <tt>Argument</tt> to convert.
  1805.  * @return Conversion
  1806.  */
  1807. Type.prototype.parse = function(arg) {
  1808.   throw new Error('Not implemented');
  1809. };
  1810.  
  1811. /**
  1812.  * A convenience method for times when you don't have an argument to parse
  1813.  * but instead have a string.
  1814.  * @see #parse(arg)
  1815.  */
  1816. Type.prototype.parseString = function(str) {
  1817.   return this.parse(new Argument(str));
  1818. },
  1819.  
  1820. /**
  1821.  * The plug-in system, and other things need to know what this type is
  1822.  * called. The name alone is not enough to fully specify a type. Types like
  1823.  * 'selection' and 'deferred' need extra data, however this function returns
  1824.  * only the name, not the extra data.
  1825.  */
  1826. Type.prototype.name = undefined;
  1827.  
  1828. /**
  1829.  * If there is some concept of a higher value, return it,
  1830.  * otherwise return undefined.
  1831.  */
  1832. Type.prototype.increment = function(value) {
  1833.   return undefined;
  1834. };
  1835.  
  1836. /**
  1837.  * If there is some concept of a lower value, return it,
  1838.  * otherwise return undefined.
  1839.  */
  1840. Type.prototype.decrement = function(value) {
  1841.   return undefined;
  1842. };
  1843.  
  1844. /**
  1845.  * There is interesting information (like predictions) in a conversion of
  1846.  * nothing, the output of this can sometimes be customized.
  1847.  * @return Conversion
  1848.  */
  1849. Type.prototype.getDefault = undefined;
  1850.  
  1851. types.Type = Type;
  1852.  
  1853. /**
  1854.  * Private registry of types
  1855.  * Invariant: types[name] = type.name
  1856.  */
  1857. var registeredTypes = {};
  1858.  
  1859. types.getTypeNames = function() {
  1860.   return Object.keys(registeredTypes);
  1861. };
  1862.  
  1863. /**
  1864.  * Add a new type to the list available to the system.
  1865.  * You can pass 2 things to this function - either an instance of Type, in
  1866.  * which case we return this instance when #getType() is called with a 'name'
  1867.  * that matches type.name.
  1868.  * Also you can pass in a constructor (i.e. function) in which case when
  1869.  * #getType() is called with a 'name' that matches Type.prototype.name we will
  1870.  * pass the typeSpec into this constructor.
  1871.  */
  1872. types.registerType = function(type) {
  1873.   if (typeof type === 'object') {
  1874.     if (type instanceof Type) {
  1875.       if (!type.name) {
  1876.         throw new Error('All registered types must have a name');
  1877.       }
  1878.       registeredTypes[type.name] = type;
  1879.     }
  1880.     else {
  1881.       throw new Error('Can\'t registerType using: ' + type);
  1882.     }
  1883.   }
  1884.   else if (typeof type === 'function') {
  1885.     if (!type.prototype.name) {
  1886.       throw new Error('All registered types must have a name');
  1887.     }
  1888.     registeredTypes[type.prototype.name] = type;
  1889.   }
  1890.   else {
  1891.     throw new Error('Unknown type: ' + type);
  1892.   }
  1893. };
  1894.  
  1895. types.registerTypes = function registerTypes(newTypes) {
  1896.   Object.keys(newTypes).forEach(function(name) {
  1897.     var type = newTypes[name];
  1898.     type.name = name;
  1899.     newTypes.registerType(type);
  1900.   });
  1901. };
  1902.  
  1903. /**
  1904.  * Remove a type from the list available to the system
  1905.  */
  1906. types.deregisterType = function(type) {
  1907.   delete registeredTypes[type.name];
  1908. };
  1909.  
  1910. /**
  1911.  * Find a type, previously registered using #registerType()
  1912.  */
  1913. types.getType = function(typeSpec) {
  1914.   var type;
  1915.   if (typeof typeSpec === 'string') {
  1916.     type = registeredTypes[typeSpec];
  1917.     if (typeof type === 'function') {
  1918.       type = new type();
  1919.     }
  1920.     return type;
  1921.   }
  1922.  
  1923.   if (typeof typeSpec === 'object') {
  1924.     if (!typeSpec.name) {
  1925.       throw new Error('Missing \'name\' member to typeSpec');
  1926.     }
  1927.  
  1928.     type = registeredTypes[typeSpec.name];
  1929.     if (typeof type === 'function') {
  1930.       type = new type(typeSpec);
  1931.     }
  1932.     return type;
  1933.   }
  1934.  
  1935.   throw new Error('Can\'t extract type from ' + typeSpec);
  1936. };
  1937.  
  1938.  
  1939. });
  1940. /*
  1941.  * Copyright 2009-2011 Mozilla Foundation and contributors
  1942.  * Licensed under the New BSD license. See LICENSE.txt or:
  1943.  * http://opensource.org/licenses/BSD-3-Clause
  1944.  */
  1945.  
  1946. define('gcli/argument', ['require', 'exports', 'module' ], function(require, exports, module) {
  1947. var argument = exports;
  1948.  
  1949.  
  1950. /**
  1951.  * We record where in the input string an argument comes so we can report
  1952.  * errors against those string positions.
  1953.  * @param text The string (trimmed) that contains the argument
  1954.  * @param prefix Knowledge of quotation marks and whitespace used prior to the
  1955.  * text in the input string allows us to re-generate the original input from
  1956.  * the arguments.
  1957.  * @param suffix Any quotation marks and whitespace used after the text.
  1958.  * Whitespace is normally placed in the prefix to the succeeding argument, but
  1959.  * can be used here when this is the last argument.
  1960.  * @constructor
  1961.  */
  1962. function Argument(text, prefix, suffix) {
  1963.   if (text === undefined) {
  1964.     this.text = '';
  1965.     this.prefix = '';
  1966.     this.suffix = '';
  1967.   }
  1968.   else {
  1969.     this.text = text;
  1970.     this.prefix = prefix !== undefined ? prefix : '';
  1971.     this.suffix = suffix !== undefined ? suffix : '';
  1972.   }
  1973. }
  1974.  
  1975. /**
  1976.  * Return the result of merging these arguments.
  1977.  * case and some of the arguments are in quotation marks?
  1978.  */
  1979. Argument.prototype.merge = function(following) {
  1980.   // Is it possible that this gets called when we're merging arguments
  1981.   // for the single string?
  1982.   return new Argument(
  1983.     this.text + this.suffix + following.prefix + following.text,
  1984.     this.prefix, following.suffix);
  1985. };
  1986.  
  1987. /**
  1988.  * Returns a new Argument like this one but with the text set to
  1989.  * <tt>replText</tt> and the end adjusted to fit.
  1990.  * @param replText Text to replace the old text value
  1991.  */
  1992. Argument.prototype.beget = function(replText, options) {
  1993.   var prefix = this.prefix;
  1994.   var suffix = this.suffix;
  1995.  
  1996.   var quote = (replText.indexOf(' ') >= 0 || replText.length == 0) ?
  1997.       '\'' : '';
  1998.  
  1999.   if (options) {
  2000.     prefix = (options.prefixSpace ? ' ' : '') + quote;
  2001.     suffix = quote;
  2002.   }
  2003.  
  2004.   return new Argument(replText, prefix, suffix);
  2005. };
  2006.  
  2007. /**
  2008.  * Is there any visible content to this argument?
  2009.  */
  2010. Argument.prototype.isBlank = function() {
  2011.   return this.text === '' &&
  2012.       this.prefix.trim() === '' &&
  2013.       this.suffix.trim() === '';
  2014. };
  2015.  
  2016. /**
  2017.  * We need to keep track of which assignment we've been assigned to
  2018.  */
  2019. Argument.prototype.assign = function(assignment) {
  2020.   this.assignment = assignment;
  2021. };
  2022.  
  2023. /**
  2024.  * Sub-classes of Argument are collections of arguments, getArgs() gets access
  2025.  * to the members of the collection in order to do things like re-create input
  2026.  * command lines. For the simple Argument case it's just an array containing
  2027.  * only this.
  2028.  */
  2029. Argument.prototype.getArgs = function() {
  2030.   return [ this ];
  2031. };
  2032.  
  2033. /**
  2034.  * We define equals to mean all arg properties are strict equals.
  2035.  * Used by Conversion.argEquals and Conversion.equals and ultimately
  2036.  * Assignment.equals to avoid reporting a change event when a new conversion
  2037.  * is assigned.
  2038.  */
  2039. Argument.prototype.equals = function(that) {
  2040.   if (this === that) {
  2041.     return true;
  2042.   }
  2043.   if (that == null || !(that instanceof Argument)) {
  2044.     return false;
  2045.   }
  2046.  
  2047.   return this.text === that.text &&
  2048.        this.prefix === that.prefix && this.suffix === that.suffix;
  2049. };
  2050.  
  2051. /**
  2052.  * Helper when we're putting arguments back together
  2053.  */
  2054. Argument.prototype.toString = function() {
  2055.   // BUG 664207: We should re-escape escaped characters
  2056.   // But can we do that reliably?
  2057.   return this.prefix + this.text + this.suffix;
  2058. };
  2059.  
  2060. /**
  2061.  * Merge an array of arguments into a single argument.
  2062.  * All Arguments in the array are expected to have the same emitter
  2063.  */
  2064. Argument.merge = function(argArray, start, end) {
  2065.   start = (start === undefined) ? 0 : start;
  2066.   end = (end === undefined) ? argArray.length : end;
  2067.  
  2068.   var joined;
  2069.   for (var i = start; i < end; i++) {
  2070.     var arg = argArray[i];
  2071.     if (!joined) {
  2072.       joined = arg;
  2073.     }
  2074.     else {
  2075.       joined = joined.merge(arg);
  2076.     }
  2077.   }
  2078.   return joined;
  2079. };
  2080.  
  2081. argument.Argument = Argument;
  2082.  
  2083.  
  2084. /**
  2085.  * ScriptArgument is a marker that the argument is designed to be Javascript.
  2086.  * It also implements the special rules that spaces after the { or before the
  2087.  * } are part of the pre/suffix rather than the content.
  2088.  */
  2089. function ScriptArgument(text, prefix, suffix) {
  2090.   this.text = text;
  2091.   this.prefix = prefix !== undefined ? prefix : '';
  2092.   this.suffix = suffix !== undefined ? suffix : '';
  2093.  
  2094.   while (this.text.charAt(0) === ' ') {
  2095.     this.prefix = this.prefix + ' ';
  2096.     this.text = this.text.substring(1);
  2097.   }
  2098.  
  2099.   while (this.text.charAt(this.text.length - 1) === ' ') {
  2100.     this.suffix = ' ' + this.suffix;
  2101.     this.text = this.text.slice(0, -1);
  2102.   }
  2103. }
  2104.  
  2105. ScriptArgument.prototype = Object.create(Argument.prototype);
  2106.  
  2107. /**
  2108.  * Returns a new Argument like this one but with the text set to
  2109.  * <tt>replText</tt> and the end adjusted to fit.
  2110.  * @param replText Text to replace the old text value
  2111.  */
  2112. ScriptArgument.prototype.beget = function(replText, options) {
  2113.   var prefix = this.prefix;
  2114.   var suffix = this.suffix;
  2115.  
  2116.   var quote = (replText.indexOf(' ') >= 0 || replText.length == 0) ?
  2117.       '\'' : '';
  2118.  
  2119.   if (options && options.normalize) {
  2120.     prefix = '{ ';
  2121.     suffix = ' }';
  2122.   }
  2123.  
  2124.   return new ScriptArgument(replText, prefix, suffix);
  2125. };
  2126.  
  2127. argument.ScriptArgument = ScriptArgument;
  2128.  
  2129.  
  2130. /**
  2131.  * Commands like 'echo' with a single string argument, and used with the
  2132.  * special format like: 'echo a b c' effectively have a number of arguments
  2133.  * merged together.
  2134.  */
  2135. function MergedArgument(args, start, end) {
  2136.   if (!Array.isArray(args)) {
  2137.     throw new Error('args is not an array of Arguments');
  2138.   }
  2139.  
  2140.   if (start === undefined) {
  2141.     this.args = args;
  2142.   }
  2143.   else {
  2144.     this.args = args.slice(start, end);
  2145.   }
  2146.  
  2147.   var arg = Argument.merge(this.args);
  2148.   this.text = arg.text;
  2149.   this.prefix = arg.prefix;
  2150.   this.suffix = arg.suffix;
  2151. }
  2152.  
  2153. MergedArgument.prototype = Object.create(Argument.prototype);
  2154.  
  2155. /**
  2156.  * Keep track of which assignment we've been assigned to, and allow the
  2157.  * original args to do the same.
  2158.  */
  2159. MergedArgument.prototype.assign = function(assignment) {
  2160.   this.args.forEach(function(arg) {
  2161.     arg.assign(assignment);
  2162.   }, this);
  2163.  
  2164.   this.assignment = assignment;
  2165. };
  2166.  
  2167. MergedArgument.prototype.getArgs = function() {
  2168.   return this.args;
  2169. };
  2170.  
  2171. MergedArgument.prototype.equals = function(that) {
  2172.   if (this === that) {
  2173.     return true;
  2174.   }
  2175.   if (that == null || !(that instanceof MergedArgument)) {
  2176.     return false;
  2177.   }
  2178.  
  2179.   // We might need to add a check that args is the same here
  2180.  
  2181.   return this.text === that.text &&
  2182.        this.prefix === that.prefix && this.suffix === that.suffix;
  2183. };
  2184.  
  2185. argument.MergedArgument = MergedArgument;
  2186.  
  2187.  
  2188. /**
  2189.  * TrueNamedArguments are for when we have an argument like --verbose which
  2190.  * has a boolean value, and thus the opposite of '--verbose' is ''.
  2191.  */
  2192. function TrueNamedArgument(name, arg) {
  2193.   this.arg = arg;
  2194.   this.text = arg ? arg.text : '--' + name;
  2195.   this.prefix = arg ? arg.prefix : ' ';
  2196.   this.suffix = arg ? arg.suffix : '';
  2197. }
  2198.  
  2199. TrueNamedArgument.prototype = Object.create(Argument.prototype);
  2200.  
  2201. TrueNamedArgument.prototype.assign = function(assignment) {
  2202.   if (this.arg) {
  2203.     this.arg.assign(assignment);
  2204.   }
  2205.   this.assignment = assignment;
  2206. };
  2207.  
  2208. TrueNamedArgument.prototype.getArgs = function() {
  2209.   // NASTY! getArgs has a fairly specific use: in removing used arguments
  2210.   // from a command line. Unlike other arguments which are EITHER used
  2211.   // in assignments directly OR grouped in things like MergedArguments,
  2212.   // TrueNamedArgument is used raw from the UI, or composed of another arg
  2213.   // from the CLI, so we return both here so they can both be removed.
  2214.   return this.arg ? [ this, this.arg ] : [ this ];
  2215. };
  2216.  
  2217. TrueNamedArgument.prototype.equals = function(that) {
  2218.   if (this === that) {
  2219.     return true;
  2220.   }
  2221.   if (that == null || !(that instanceof TrueNamedArgument)) {
  2222.     return false;
  2223.   }
  2224.  
  2225.   return this.text === that.text &&
  2226.        this.prefix === that.prefix && this.suffix === that.suffix;
  2227. };
  2228.  
  2229. argument.TrueNamedArgument = TrueNamedArgument;
  2230.  
  2231.  
  2232. /**
  2233.  * FalseNamedArguments are for when we don't have an argument like --verbose
  2234.  * which has a boolean value, and thus the opposite of '' is '--verbose'.
  2235.  */
  2236. function FalseNamedArgument() {
  2237.   this.text = '';
  2238.   this.prefix = '';
  2239.   this.suffix = '';
  2240. }
  2241.  
  2242. FalseNamedArgument.prototype = Object.create(Argument.prototype);
  2243.  
  2244. FalseNamedArgument.prototype.getArgs = function() {
  2245.   return [ ];
  2246. };
  2247.  
  2248. FalseNamedArgument.prototype.equals = function(that) {
  2249.   if (this === that) {
  2250.     return true;
  2251.   }
  2252.   if (that == null || !(that instanceof FalseNamedArgument)) {
  2253.     return false;
  2254.   }
  2255.  
  2256.   return this.text === that.text &&
  2257.        this.prefix === that.prefix && this.suffix === that.suffix;
  2258. };
  2259.  
  2260. argument.FalseNamedArgument = FalseNamedArgument;
  2261.  
  2262.  
  2263. /**
  2264.  * A named argument is for cases where we have input in one of the following
  2265.  * formats:
  2266.  * <ul>
  2267.  * <li>--param value
  2268.  * <li>-p value
  2269.  * <li>--pa value
  2270.  * <li>-p:value
  2271.  * <li>--param=value
  2272.  * <li>etc
  2273.  * </ul>
  2274.  * The general format is:
  2275.  * /--?{unique-param-name-prefix}[ :=]{value}/
  2276.  * We model this as a normal argument but with a long prefix.
  2277.  */
  2278. function NamedArgument(nameArg, valueArg) {
  2279.   this.nameArg = nameArg;
  2280.   this.valueArg = valueArg;
  2281.  
  2282.   this.text = valueArg.text;
  2283.   this.prefix = nameArg.toString() + valueArg.prefix;
  2284.   this.suffix = valueArg.suffix;
  2285. }
  2286.  
  2287. NamedArgument.prototype = Object.create(Argument.prototype);
  2288.  
  2289. NamedArgument.prototype.assign = function(assignment) {
  2290.   this.nameArg.assign(assignment);
  2291.   this.valueArg.assign(assignment);
  2292.   this.assignment = assignment;
  2293. };
  2294.  
  2295. NamedArgument.prototype.getArgs = function() {
  2296.   return [ this.nameArg, this.valueArg ];
  2297. };
  2298.  
  2299. NamedArgument.prototype.equals = function(that) {
  2300.   if (this === that) {
  2301.     return true;
  2302.   }
  2303.   if (that == null) {
  2304.     return false;
  2305.   }
  2306.  
  2307.   if (!(that instanceof NamedArgument)) {
  2308.     return false;
  2309.   }
  2310.  
  2311.   // We might need to add a check that nameArg and valueArg are the same
  2312.  
  2313.   return this.text === that.text &&
  2314.        this.prefix === that.prefix && this.suffix === that.suffix;
  2315. };
  2316.  
  2317. argument.NamedArgument = NamedArgument;
  2318.  
  2319.  
  2320. /**
  2321.  * An argument the groups together a number of plain arguments together so they
  2322.  * can be jointly assigned to a single array parameter
  2323.  */
  2324. function ArrayArgument() {
  2325.   this.args = [];
  2326. }
  2327.  
  2328. ArrayArgument.prototype = Object.create(Argument.prototype);
  2329.  
  2330. ArrayArgument.prototype.addArgument = function(arg) {
  2331.   this.args.push(arg);
  2332. };
  2333.  
  2334. ArrayArgument.prototype.addArguments = function(args) {
  2335.   Array.prototype.push.apply(this.args, args);
  2336. };
  2337.  
  2338. ArrayArgument.prototype.getArguments = function() {
  2339.   return this.args;
  2340. };
  2341.  
  2342. ArrayArgument.prototype.assign = function(assignment) {
  2343.   this.args.forEach(function(arg) {
  2344.     arg.assign(assignment);
  2345.   }, this);
  2346.  
  2347.   this.assignment = assignment;
  2348. };
  2349.  
  2350. ArrayArgument.prototype.getArgs = function() {
  2351.   return this.args;
  2352. };
  2353.  
  2354. ArrayArgument.prototype.equals = function(that) {
  2355.   if (this === that) {
  2356.     return true;
  2357.   }
  2358.   if (that == null) {
  2359.     return false;
  2360.   }
  2361.  
  2362.   if (!(that instanceof ArrayArgument)) {
  2363.     return false;
  2364.   }
  2365.  
  2366.   if (this.args.length !== that.args.length) {
  2367.     return false;
  2368.   }
  2369.  
  2370.   for (var i = 0; i < this.args.length; i++) {
  2371.     if (!this.args[i].equals(that.args[i])) {
  2372.       return false;
  2373.     }
  2374.   }
  2375.  
  2376.   return true;
  2377. };
  2378.  
  2379. /**
  2380.  * Helper when we're putting arguments back together
  2381.  */
  2382. ArrayArgument.prototype.toString = function() {
  2383.   return '{' + this.args.map(function(arg) {
  2384.     return arg.toString();
  2385.   }, this).join(',') + '}';
  2386. };
  2387.  
  2388. argument.ArrayArgument = ArrayArgument;
  2389.  
  2390.  
  2391. });
  2392. /*
  2393.  * Copyright 2009-2011 Mozilla Foundation and contributors
  2394.  * Licensed under the New BSD license. See LICENSE.txt or:
  2395.  * http://opensource.org/licenses/BSD-3-Clause
  2396.  */
  2397.  
  2398. define('gcli/types/basic', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types', 'gcli/argument'], function(require, exports, module) {
  2399.  
  2400.  
  2401. var l10n = require('gcli/l10n');
  2402. var types = require('gcli/types');
  2403. var Type = require('gcli/types').Type;
  2404. var Status = require('gcli/types').Status;
  2405. var Conversion = require('gcli/types').Conversion;
  2406. var ArrayConversion = require('gcli/types').ArrayConversion;
  2407.  
  2408. var Argument = require('gcli/argument').Argument;
  2409. var TrueNamedArgument = require('gcli/argument').TrueNamedArgument;
  2410. var FalseNamedArgument = require('gcli/argument').FalseNamedArgument;
  2411. var ArrayArgument = require('gcli/argument').ArrayArgument;
  2412.  
  2413.  
  2414. /**
  2415.  * Registration and de-registration.
  2416.  */
  2417. exports.startup = function() {
  2418.   types.registerType(StringType);
  2419.   types.registerType(NumberType);
  2420.   types.registerType(BooleanType);
  2421.   types.registerType(BlankType);
  2422.   types.registerType(SelectionType);
  2423.   types.registerType(DeferredType);
  2424.   types.registerType(ArrayType);
  2425. };
  2426.  
  2427. exports.shutdown = function() {
  2428.   types.unregisterType(StringType);
  2429.   types.unregisterType(NumberType);
  2430.   types.unregisterType(BooleanType);
  2431.   types.unregisterType(BlankType);
  2432.   types.unregisterType(SelectionType);
  2433.   types.unregisterType(DeferredType);
  2434.   types.unregisterType(ArrayType);
  2435. };
  2436.  
  2437.  
  2438. /**
  2439.  * 'string' the most basic string type that doesn't need to convert
  2440.  */
  2441. function StringType(typeSpec) {
  2442.   if (typeSpec != null) {
  2443.     throw new Error('StringType can not be customized');
  2444.   }
  2445. }
  2446.  
  2447. StringType.prototype = Object.create(Type.prototype);
  2448.  
  2449. StringType.prototype.stringify = function(value) {
  2450.   if (value == null) {
  2451.     return '';
  2452.   }
  2453.   return value.toString();
  2454. };
  2455.  
  2456. StringType.prototype.parse = function(arg) {
  2457.   if (arg.text == null || arg.text === '') {
  2458.     return new Conversion(null, arg, Status.INCOMPLETE, '');
  2459.   }
  2460.   return new Conversion(arg.text, arg);
  2461. };
  2462.  
  2463. StringType.prototype.name = 'string';
  2464.  
  2465. exports.StringType = StringType;
  2466.  
  2467.  
  2468. /**
  2469.  * We don't currently plan to distinguish between integers and floats
  2470.  */
  2471. function NumberType(typeSpec) {
  2472.   if (typeSpec) {
  2473.     this._min = typeSpec.min;
  2474.     this._max = typeSpec.max;
  2475.     this._step = typeSpec.step || 1;
  2476.   }
  2477.   else {
  2478.     this._step = 1;
  2479.   }
  2480. }
  2481.  
  2482. NumberType.prototype = Object.create(Type.prototype);
  2483.  
  2484. NumberType.prototype.stringify = function(value) {
  2485.   if (value == null) {
  2486.     return '';
  2487.   }
  2488.   return '' + value;
  2489. };
  2490.  
  2491. NumberType.prototype.getMin = function() {
  2492.   if (this._min) {
  2493.     if (typeof this._min === 'function') {
  2494.       return this._min();
  2495.     }
  2496.     if (typeof this._min === 'number') {
  2497.       return this._min;
  2498.     }
  2499.   }
  2500.   return 0;
  2501. };
  2502.  
  2503. NumberType.prototype.getMax = function() {
  2504.   if (this._max) {
  2505.     if (typeof this._max === 'function') {
  2506.       return this._max();
  2507.     }
  2508.     if (typeof this._max === 'number') {
  2509.       return this._max;
  2510.     }
  2511.   }
  2512.   return undefined;
  2513. };
  2514.  
  2515. NumberType.prototype.parse = function(arg) {
  2516.   if (arg.text.replace(/\s/g, '').length === 0) {
  2517.     return new Conversion(null, arg, Status.INCOMPLETE, '');
  2518.   }
  2519.  
  2520.   var value = parseInt(arg.text, 10);
  2521.   if (isNaN(value)) {
  2522.     return new Conversion(null, arg, Status.ERROR,
  2523.         l10n.lookupFormat('typesNumberNan', [ arg.text ]));
  2524.   }
  2525.  
  2526.   if (this.getMax() != null && value > this.getMax()) {
  2527.     return new Conversion(null, arg, Status.ERROR,
  2528.         l10n.lookupFormat('typesNumberMax', [ value, this.getMax() ]));
  2529.   }
  2530.  
  2531.   if (value < this.getMin()) {
  2532.     return new Conversion(null, arg, Status.ERROR,
  2533.         l10n.lookupFormat('typesNumberMin', [ value, this.getMin() ]));
  2534.   }
  2535.  
  2536.   return new Conversion(value, arg);
  2537. };
  2538.  
  2539. NumberType.prototype.decrement = function(value) {
  2540.   if (typeof value !== 'number' || isNaN(value)) {
  2541.     return this.getMax() || 1;
  2542.   }
  2543.   var newValue = value - this._step;
  2544.   // Snap to the nearest incremental of the step
  2545.   newValue = Math.ceil(newValue / this._step) * this._step;
  2546.   return this._boundsCheck(newValue);
  2547. };
  2548.  
  2549. NumberType.prototype.increment = function(value) {
  2550.   if (typeof value !== 'number' || isNaN(value)) {
  2551.     return this.getMin();
  2552.   }
  2553.   var newValue = value + this._step;
  2554.   // Snap to the nearest incremental of the step
  2555.   newValue = Math.floor(newValue / this._step) * this._step;
  2556.   if (this.getMax() == null) {
  2557.     return newValue;
  2558.   }
  2559.   return this._boundsCheck(newValue);
  2560. };
  2561.  
  2562. /**
  2563.  * Return the input value so long as it is within the max/min bounds. If it is
  2564.  * lower than the minimum, return the minimum. If it is bigger than the maximum
  2565.  * then return the maximum.
  2566.  */
  2567. NumberType.prototype._boundsCheck = function(value) {
  2568.   var min = this.getMin();
  2569.   if (value < min) {
  2570.     return min;
  2571.   }
  2572.   var max = this.getMax();
  2573.   if (value > max) {
  2574.     return max;
  2575.   }
  2576.   return value;
  2577. };
  2578.  
  2579. NumberType.prototype.name = 'number';
  2580.  
  2581. exports.NumberType = NumberType;
  2582.  
  2583. /**
  2584.  * One of a known set of options
  2585.  */
  2586. function SelectionType(typeSpec) {
  2587.   if (typeSpec) {
  2588.     Object.keys(typeSpec).forEach(function(key) {
  2589.       this[key] = typeSpec[key];
  2590.     }, this);
  2591.   }
  2592. }
  2593.  
  2594. SelectionType.prototype = Object.create(Type.prototype);
  2595.  
  2596. SelectionType.prototype.stringify = function(value) {
  2597.   var name = null;
  2598.   var lookup = this.getLookup();
  2599.   lookup.some(function(item) {
  2600.     var test = (item.value == null) ? item : item.value;
  2601.     if (test === value) {
  2602.       name = item.name;
  2603.       return true;
  2604.     }
  2605.     return false;
  2606.   }, this);
  2607.   return name;
  2608. };
  2609.  
  2610. /**
  2611.  * There are several ways to get selection data. This unifies them into one
  2612.  * single function.
  2613.  * @return A map of names to values.
  2614.  */
  2615. SelectionType.prototype.getLookup = function() {
  2616.   if (this.lookup) {
  2617.     if (typeof this.lookup === 'function') {
  2618.       return this.lookup();
  2619.     }
  2620.     return this.lookup;
  2621.   }
  2622.  
  2623.   if (Array.isArray(this.data)) {
  2624.     this.lookup = this._dataToLookup(this.data);
  2625.     return this.lookup;
  2626.   }
  2627.  
  2628.   if (typeof(this.data) === 'function') {
  2629.     return this._dataToLookup(this.data());
  2630.   }
  2631.  
  2632.   throw new Error('SelectionType has no data');
  2633. };
  2634.  
  2635. /**
  2636.  * Selection can be provided with either a lookup object (in the 'lookup'
  2637.  * property) or an array of strings (in the 'data' property). Internally we
  2638.  * always use lookup, so we need a way to convert a 'data' array to a lookup.
  2639.  */
  2640. SelectionType.prototype._dataToLookup = function(data) {
  2641.   return data.map(function(option) {
  2642.     return { name: option, value: option };
  2643.   });
  2644. };
  2645.  
  2646. /**
  2647.  * Return a list of possible completions for the given arg.
  2648.  * This code is very similar to CommandType._findPredictions(). If you are
  2649.  * making changes to this code, you should check there too.
  2650.  * @param arg The initial input to match
  2651.  * @return A trimmed lookup table of string:value pairs
  2652.  */
  2653. SelectionType.prototype._findPredictions = function(arg) {
  2654.   var predictions = [];
  2655.   this.getLookup().forEach(function(item) {
  2656.     if (item.name.indexOf(arg.text) === 0) {
  2657.       predictions.push(item);
  2658.     }
  2659.   }, this);
  2660.   return predictions;
  2661. };
  2662.  
  2663. SelectionType.prototype.parse = function(arg) {
  2664.   var predictions = this._findPredictions(arg);
  2665.  
  2666.   if (predictions.length === 1 && predictions[0].name === arg.text) {
  2667.     var value = predictions[0].value ? predictions[0].value : predictions[0];
  2668.     return new Conversion(value, arg);
  2669.   }
  2670.  
  2671.   // This is something of a hack it basically allows us to tell the
  2672.   // setting type to forget its last setting hack.
  2673.   if (this.noMatch) {
  2674.     this.noMatch();
  2675.   }
  2676.  
  2677.   if (predictions.length > 0) {
  2678.     // Especially at startup, predictions live over the time that things
  2679.     // change so we provide a completion function rather than completion
  2680.     // values.
  2681.     // This was primarily designed for commands, which have since moved
  2682.     // into their own type, so technically we could remove this code,
  2683.     // except that it provides more up-to-date answers, and it's hard to
  2684.     // predict when it will be required.
  2685.     var predictFunc = function() {
  2686.       return this._findPredictions(arg);
  2687.     }.bind(this);
  2688.     return new Conversion(null, arg, Status.INCOMPLETE, '', predictFunc);
  2689.   }
  2690.  
  2691.   return new Conversion(null, arg, Status.ERROR,
  2692.       l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]));
  2693. };
  2694.  
  2695. /**
  2696.  * For selections, up is down and black is white. It's like this, given a list
  2697.  * [ a, b, c, d ], it's natural to think that it starts at the top and that
  2698.  * going up the list, moves towards 'a'. However 'a' has the lowest index, so
  2699.  * for SelectionType, up is down and down is up.
  2700.  * Sorry.
  2701.  */
  2702. SelectionType.prototype.decrement = function(value) {
  2703.   var lookup = this.getLookup();
  2704.   var index = this._findValue(lookup, value);
  2705.   if (index === -1) {
  2706.     index = 0;
  2707.   }
  2708.   index++;
  2709.   if (index >= lookup.length) {
  2710.     index = 0;
  2711.   }
  2712.   return lookup[index].value;
  2713. };
  2714.  
  2715. /**
  2716.  * See note on SelectionType.decrement()
  2717.  */
  2718. SelectionType.prototype.increment = function(value) {
  2719.   var lookup = this.getLookup();
  2720.   var index = this._findValue(lookup, value);
  2721.   if (index === -1) {
  2722.     // For an increment operation when there is nothing to start from, we
  2723.     // want to start from the top, i.e. index 0, so the value before we
  2724.     // 'increment' (see note above) must be 1.
  2725.     index = 1;
  2726.   }
  2727.   index--;
  2728.   if (index < 0) {
  2729.     index = lookup.length - 1;
  2730.   }
  2731.   return lookup[index].value;
  2732. };
  2733.  
  2734. /**
  2735.  * Walk through an array of { name:.., value:... } objects looking for a
  2736.  * matching value (using strict equality), returning the matched index (or -1
  2737.  * if not found).
  2738.  * @param lookup Array of objects with name/value properties to search through
  2739.  * @param value The value to search for
  2740.  * @return The index at which the match was found, or -1 if no match was found
  2741.  */
  2742. SelectionType.prototype._findValue = function(lookup, value) {
  2743.   var index = -1;
  2744.   for (var i = 0; i < lookup.length; i++) {
  2745.     var pair = lookup[i];
  2746.     if (pair.value === value) {
  2747.       index = i;
  2748.       break;
  2749.     }
  2750.   }
  2751.   return index;
  2752. };
  2753.  
  2754. SelectionType.prototype.name = 'selection';
  2755.  
  2756. exports.SelectionType = SelectionType;
  2757.  
  2758.  
  2759. /**
  2760.  * true/false values
  2761.  */
  2762. function BooleanType(typeSpec) {
  2763.   if (typeSpec != null) {
  2764.     throw new Error('BooleanType can not be customized');
  2765.   }
  2766. }
  2767.  
  2768. BooleanType.prototype = Object.create(SelectionType.prototype);
  2769.  
  2770. BooleanType.prototype.lookup = [
  2771.   { name: 'true', value: true },
  2772.   { name: 'false', value: false }
  2773. ];
  2774.  
  2775. BooleanType.prototype.parse = function(arg) {
  2776.   if (arg instanceof TrueNamedArgument) {
  2777.     return new Conversion(true, arg);
  2778.   }
  2779.   if (arg instanceof FalseNamedArgument) {
  2780.     return new Conversion(false, arg);
  2781.   }
  2782.   return SelectionType.prototype.parse.call(this, arg);
  2783. };
  2784.  
  2785. BooleanType.prototype.stringify = function(value) {
  2786.   return '' + value;
  2787. };
  2788.  
  2789. BooleanType.prototype.getDefault = function() {
  2790.   return new Conversion(false, new Argument(''));
  2791. };
  2792.  
  2793. BooleanType.prototype.name = 'boolean';
  2794.  
  2795. exports.BooleanType = BooleanType;
  2796.  
  2797.  
  2798. /**
  2799.  * A type for "we don't know right now, but hope to soon".
  2800.  */
  2801. function DeferredType(typeSpec) {
  2802.   if (typeof typeSpec.defer !== 'function') {
  2803.     throw new Error('Instances of DeferredType need typeSpec.defer to be a function that returns a type');
  2804.   }
  2805.   Object.keys(typeSpec).forEach(function(key) {
  2806.     this[key] = typeSpec[key];
  2807.   }, this);
  2808. }
  2809.  
  2810. DeferredType.prototype = Object.create(Type.prototype);
  2811.  
  2812. DeferredType.prototype.stringify = function(value) {
  2813.   return this.defer().stringify(value);
  2814. };
  2815.  
  2816. DeferredType.prototype.parse = function(arg) {
  2817.   return this.defer().parse(arg);
  2818. };
  2819.  
  2820. DeferredType.prototype.decrement = function(value) {
  2821.   var deferred = this.defer();
  2822.   return (deferred.decrement ? deferred.decrement(value) : undefined);
  2823. };
  2824.  
  2825. DeferredType.prototype.increment = function(value) {
  2826.   var deferred = this.defer();
  2827.   return (deferred.increment ? deferred.increment(value) : undefined);
  2828. };
  2829.  
  2830. DeferredType.prototype.increment = function(value) {
  2831.   var deferred = this.defer();
  2832.   return (deferred.increment ? deferred.increment(value) : undefined);
  2833. };
  2834.  
  2835. DeferredType.prototype.name = 'deferred';
  2836.  
  2837. exports.DeferredType = DeferredType;
  2838.  
  2839.  
  2840. /**
  2841.  * 'blank' is a type for use with DeferredType when we don't know yet.
  2842.  * It should not be used anywhere else.
  2843.  */
  2844. function BlankType(typeSpec) {
  2845.   if (typeSpec != null) {
  2846.     throw new Error('BlankType can not be customized');
  2847.   }
  2848. }
  2849.  
  2850. BlankType.prototype = Object.create(Type.prototype);
  2851.  
  2852. BlankType.prototype.stringify = function(value) {
  2853.   return '';
  2854. };
  2855.  
  2856. BlankType.prototype.parse = function(arg) {
  2857.   return new Conversion(null, arg);
  2858. };
  2859.  
  2860. BlankType.prototype.name = 'blank';
  2861.  
  2862. exports.BlankType = BlankType;
  2863.  
  2864.  
  2865. /**
  2866.  * A set of objects of the same type
  2867.  */
  2868. function ArrayType(typeSpec) {
  2869.   if (!typeSpec.subtype) {
  2870.     console.error('Array.typeSpec is missing subtype. Assuming string.' +
  2871.         JSON.stringify(typeSpec));
  2872.     typeSpec.subtype = 'string';
  2873.   }
  2874.  
  2875.   Object.keys(typeSpec).forEach(function(key) {
  2876.     this[key] = typeSpec[key];
  2877.   }, this);
  2878.   this.subtype = types.getType(this.subtype);
  2879. }
  2880.  
  2881. ArrayType.prototype = Object.create(Type.prototype);
  2882.  
  2883. ArrayType.prototype.stringify = function(values) {
  2884.   // BUG 664204: Check for strings with spaces and add quotes
  2885.   return values.join(' ');
  2886. };
  2887.  
  2888. ArrayType.prototype.parse = function(arg) {
  2889.   if (arg instanceof ArrayArgument) {
  2890.     var conversions = arg.getArguments().map(function(subArg) {
  2891.       var conversion = this.subtype.parse(subArg);
  2892.       // Hack alert. ArrayConversion needs to be able to answer questions
  2893.       // about the status of individual conversions in addition to the
  2894.       // overall state. This allows us to do that easily.
  2895.       subArg.conversion = conversion;
  2896.       return conversion;
  2897.     }, this);
  2898.     return new ArrayConversion(conversions, arg);
  2899.   }
  2900.   else {
  2901.     console.error('non ArrayArgument to ArrayType.parse', arg);
  2902.     throw new Error('non ArrayArgument to ArrayType.parse');
  2903.   }
  2904. };
  2905.  
  2906. ArrayType.prototype.getDefault = function() {
  2907.   return new ArrayConversion([], new ArrayArgument());
  2908. };
  2909.  
  2910. ArrayType.prototype.name = 'array';
  2911.  
  2912. exports.ArrayType = ArrayType;
  2913.  
  2914.  
  2915. });
  2916. /*
  2917.  * Copyright 2009-2011 Mozilla Foundation and contributors
  2918.  * Licensed under the New BSD license. See LICENSE.txt or:
  2919.  * http://opensource.org/licenses/BSD-3-Clause
  2920.  */
  2921.  
  2922. define('gcli/types/javascript', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types'], function(require, exports, module) {
  2923.  
  2924.  
  2925. var l10n = require('gcli/l10n');
  2926. var types = require('gcli/types');
  2927.  
  2928. var Conversion = types.Conversion;
  2929. var Type = types.Type;
  2930. var Status = types.Status;
  2931.  
  2932.  
  2933. /**
  2934.  * Registration and de-registration.
  2935.  */
  2936. exports.startup = function() {
  2937.   types.registerType(JavascriptType);
  2938. };
  2939.  
  2940. exports.shutdown = function() {
  2941.   types.unregisterType(JavascriptType);
  2942. };
  2943.  
  2944. /**
  2945.  * The object against which we complete, which is usually 'window' if it exists
  2946.  * but could be something else in non-web-content environments.
  2947.  */
  2948. var globalObject;
  2949. if (typeof window !== 'undefined') {
  2950.   globalObject = window;
  2951. }
  2952.  
  2953. /**
  2954.  * Setter for the object against which JavaScript completions happen
  2955.  */
  2956. exports.setGlobalObject = function(obj) {
  2957.   globalObject = obj;
  2958. };
  2959.  
  2960. /**
  2961.  * Getter for the object against which JavaScript completions happen, for use
  2962.  * in testing
  2963.  */
  2964. exports.getGlobalObject = function() {
  2965.   return globalObject;
  2966. };
  2967.  
  2968. /**
  2969.  * Remove registration of object against which JavaScript completions happen
  2970.  */
  2971. exports.unsetGlobalObject = function() {
  2972.   globalObject = undefined;
  2973. };
  2974.  
  2975.  
  2976. /**
  2977.  * 'javascript' handles scripted input
  2978.  */
  2979. function JavascriptType(typeSpec) {
  2980.   if (typeSpec != null) {
  2981.     throw new Error('JavascriptType can not be customized');
  2982.   }
  2983. }
  2984.  
  2985. JavascriptType.prototype = Object.create(Type.prototype);
  2986.  
  2987. JavascriptType.prototype.stringify = function(value) {
  2988.   if (value == null) {
  2989.     return '';
  2990.   }
  2991.   return value;
  2992. };
  2993.  
  2994. /**
  2995.  * When sorting out completions, there is no point in displaying millions of
  2996.  * matches - this the number of matches that we aim for
  2997.  */
  2998. JavascriptType.MAX_COMPLETION_MATCHES = 10;
  2999.  
  3000. JavascriptType.prototype.parse = function(arg) {
  3001.   var typed = arg.text;
  3002.   var scope = globalObject;
  3003.  
  3004.   // Analyze the input text and find the beginning of the last part that
  3005.   // should be completed.
  3006.   var beginning = this._findCompletionBeginning(typed);
  3007.  
  3008.   // There was an error analyzing the string.
  3009.   if (beginning.err) {
  3010.     return new Conversion(typed, arg, Status.ERROR, beginning.err);
  3011.   }
  3012.  
  3013.   // If the current state is not ParseState.NORMAL, then we are inside of a
  3014.   // string which means that no completion is possible.
  3015.   if (beginning.state !== ParseState.NORMAL) {
  3016.     return new Conversion(typed, arg, Status.INCOMPLETE, '');
  3017.   }
  3018.  
  3019.   var completionPart = typed.substring(beginning.startPos);
  3020.   var properties = completionPart.split('.');
  3021.   var matchProp;
  3022.   var prop;
  3023.  
  3024.   if (properties.length > 1) {
  3025.     matchProp = properties.pop().trimLeft();
  3026.     for (var i = 0; i < properties.length; i++) {
  3027.       prop = properties[i].trim();
  3028.  
  3029.       // We can't complete on null.foo, so bail out
  3030.       if (scope == null) {
  3031.         return new Conversion(typed, arg, Status.ERROR,
  3032.                 l10n.lookup('jstypeParseScope'));
  3033.       }
  3034.  
  3035.       if (prop === '') {
  3036.         return new Conversion(typed, arg, Status.INCOMPLETE, '');
  3037.       }
  3038.  
  3039.       // Check if prop is a getter function on 'scope'. Functions can change
  3040.       // other stuff so we can't execute them to get the next object. Stop here.
  3041.       if (this._isSafeProperty(scope, prop)) {
  3042.         return new Conversion(typed, arg);
  3043.       }
  3044.  
  3045.       try {
  3046.         scope = scope[prop];
  3047.       }
  3048.       catch (ex) {
  3049.         // It would be nice to be able to report this error in some way but
  3050.         // as it can happen just when someone types '{sessionStorage.', it
  3051.         // almost doesn't really count as an error, so we ignore it
  3052.         return new Conversion(typed, arg, Status.INCOMPLETE, '');
  3053.       }
  3054.     }
  3055.   }
  3056.   else {
  3057.     matchProp = properties[0].trimLeft();
  3058.   }
  3059.  
  3060.   // If the reason we just stopped adjusting the scope was a non-simple string,
  3061.   // then we're not sure if the input is valid or invalid, so accept it
  3062.   if (prop && !prop.match(/^[0-9A-Za-z]*$/)) {
  3063.     return new Conversion(typed, arg);
  3064.   }
  3065.  
  3066.   // However if the prop was a simple string, it is an error
  3067.   if (scope == null) {
  3068.     return new Conversion(typed, arg, Status.ERROR,
  3069.         l10n.lookupFormat('jstypeParseMissing', [ prop ]));
  3070.   }
  3071.  
  3072.   // If the thing we're looking for isn't a simple string, then we're not going
  3073.   // to find it, but we're not sure if it's valid or invalid, so accept it
  3074.   if (!matchProp.match(/^[0-9A-Za-z]*$/)) {
  3075.     return new Conversion(typed, arg);
  3076.   }
  3077.  
  3078.   // Skip Iterators and Generators.
  3079.   if (this._isIteratorOrGenerator(scope)) {
  3080.     return null;
  3081.   }
  3082.  
  3083.   var matchLen = matchProp.length;
  3084.   var prefix = matchLen === 0 ? typed : typed.slice(0, -matchLen);
  3085.   var status = Status.INCOMPLETE;
  3086.   var message = '';
  3087.  
  3088.   // We really want an array of matches (for sorting) but it's easier to
  3089.   // detect existing members if we're using a map initially
  3090.   var matches = {};
  3091.  
  3092.   // We only display a maximum of MAX_COMPLETION_MATCHES, so there is no point
  3093.   // in digging up the prototype chain for matches that we're never going to
  3094.   // use. Initially look for matches directly on the object itself and then
  3095.   // look up the chain to find more
  3096.   var distUpPrototypeChain = 0;
  3097.   var root = scope;
  3098.   try {
  3099.     while (root != null &&
  3100.         Object.keys(matches).length < JavascriptType.MAX_COMPLETION_MATCHES) {
  3101.  
  3102.       Object.keys(root).forEach(function(property) {
  3103.         // Only add matching properties. Also, as we're walking up the
  3104.         // prototype chain, properties on 'higher' prototypes don't override
  3105.         // similarly named properties lower down
  3106.         if (property.indexOf(matchProp) === 0 && !(property in matches)) {
  3107.           matches[property] = {
  3108.             prop: property,
  3109.             distUpPrototypeChain: distUpPrototypeChain
  3110.           };
  3111.         }
  3112.       });
  3113.  
  3114.       distUpPrototypeChain++;
  3115.       root = Object.getPrototypeOf(root);
  3116.     }
  3117.   }
  3118.   catch (ex) {
  3119.     return new Conversion(typed, arg, Status.INCOMPLETE, '');
  3120.   }
  3121.  
  3122.   // Convert to an array for sorting, and while we're at it, note if we got
  3123.   // an exact match so we know that this input is valid
  3124.   matches = Object.keys(matches).map(function(property) {
  3125.     if (property === matchProp) {
  3126.       status = Status.VALID;
  3127.     }
  3128.     return matches[property];
  3129.   });
  3130.  
  3131.   // The sort keys are:
  3132.   // - Being on the object itself, not in the prototype chain
  3133.   // - The lack of existence of a vendor prefix
  3134.   // - The name
  3135.   matches.sort(function(m1, m2) {
  3136.     if (m1.distUpPrototypeChain !== m2.distUpPrototypeChain) {
  3137.       return m1.distUpPrototypeChain - m2.distUpPrototypeChain;
  3138.     }
  3139.     // Push all vendor prefixes to the bottom of the list
  3140.     return isVendorPrefixed(m1.prop) ?
  3141.       (isVendorPrefixed(m2.prop) ? m1.prop.localeCompare(m2.prop) : 1) :
  3142.       (isVendorPrefixed(m2.prop) ? -1 : m1.prop.localeCompare(m2.prop));
  3143.   });
  3144.  
  3145.   // Trim to size. There is a bug for doing a better job of finding matches
  3146.   // (bug 682694), but in the mean time there is a performance problem
  3147.   // associated with creating a large number of DOM nodes that few people will
  3148.   // ever read, so trim ...
  3149.   if (matches.length > JavascriptType.MAX_COMPLETION_MATCHES) {
  3150.     matches = matches.slice(0, JavascriptType.MAX_COMPLETION_MATCHES - 1);
  3151.   }
  3152.  
  3153.   // Decorate the matches with:
  3154.   // - a description
  3155.   // - a value (for the menu) and,
  3156.   // - an incomplete flag which reports if we should assume that the user isn't
  3157.   //   going to carry on the JS expression with this input so far
  3158.   var predictions = matches.map(function(match) {
  3159.     var description;
  3160.     var incomplete = true;
  3161.  
  3162.     if (this._isSafeProperty(scope, match.prop)) {
  3163.       description = '(property getter)';
  3164.     }
  3165.     else {
  3166.       try {
  3167.         var value = scope[match.prop];
  3168.  
  3169.         if (typeof value === 'function') {
  3170.           description = '(function)';
  3171.         }
  3172.         else if (typeof value === 'boolean' || typeof value === 'number') {
  3173.           description = '= ' + value;
  3174.           incomplete = false;
  3175.         }
  3176.         else if (typeof value === 'string') {
  3177.           if (value.length > 40) {
  3178.             value = value.substring(0, 37) + '…';
  3179.           }
  3180.           description = '= \'' + value + '\'';
  3181.           incomplete = false;
  3182.         }
  3183.         else {
  3184.           description = '(' + typeof value + ')';
  3185.         }
  3186.       }
  3187.       catch (ex) {
  3188.         description = '(' + l10n.lookup('jstypeParseError') + ')';
  3189.       }
  3190.     }
  3191.  
  3192.     return {
  3193.       name: prefix + match.prop,
  3194.       value: {
  3195.         name: prefix + match.prop,
  3196.         description: description
  3197.       },
  3198.       description: description,
  3199.       incomplete: incomplete
  3200.     };
  3201.   }, this);
  3202.  
  3203.   if (predictions.length === 0) {
  3204.     status = Status.ERROR;
  3205.     message = l10n.lookupFormat('jstypeParseMissing', [ matchProp ]);
  3206.   }
  3207.  
  3208.   // If the match is the only one possible, and its VALID, predict nothing
  3209.   if (predictions.length === 1 && status === Status.VALID) {
  3210.     predictions = undefined;
  3211.   }
  3212.  
  3213.   return new Conversion(typed, arg, status, message, predictions);
  3214. };
  3215.  
  3216. /**
  3217.  * Does the given property have a prefix that indicates that it is vendor
  3218.  * specific?
  3219.  */
  3220. function isVendorPrefixed(name) {
  3221.   return name.indexOf('moz') === 0 ||
  3222.          name.indexOf('webkit') === 0 ||
  3223.          name.indexOf('ms') === 0;
  3224. }
  3225.  
  3226. /**
  3227.  * Constants used in return value of _findCompletionBeginning()
  3228.  */
  3229. var ParseState = {
  3230.   NORMAL: 0,
  3231.   QUOTE: 2,
  3232.   DQUOTE: 3
  3233. };
  3234.  
  3235. var OPEN_BODY = '{[('.split('');
  3236. var CLOSE_BODY = '}])'.split('');
  3237. var OPEN_CLOSE_BODY = {
  3238.   '{': '}',
  3239.   '[': ']',
  3240.   '(': ')'
  3241. };
  3242.  
  3243. /**
  3244.  * Analyzes a given string to find the last statement that is interesting for
  3245.  * later completion.
  3246.  * @param text A string to analyze
  3247.  * @return If there was an error in the string detected, then a object like
  3248.  *   { err: 'ErrorMesssage' }
  3249.  * is returned, otherwise a object like
  3250.  *   {
  3251.  *     state: ParseState.NORMAL|ParseState.QUOTE|ParseState.DQUOTE,
  3252.  *     startPos: index of where the last statement begins
  3253.  *   }
  3254.  */
  3255. JavascriptType.prototype._findCompletionBeginning = function(text) {
  3256.   var bodyStack = [];
  3257.  
  3258.   var state = ParseState.NORMAL;
  3259.   var start = 0;
  3260.   var c;
  3261.   for (var i = 0; i < text.length; i++) {
  3262.     c = text[i];
  3263.  
  3264.     switch (state) {
  3265.       // Normal JS state.
  3266.       case ParseState.NORMAL:
  3267.         if (c === '"') {
  3268.           state = ParseState.DQUOTE;
  3269.         }
  3270.         else if (c === '\'') {
  3271.           state = ParseState.QUOTE;
  3272.         }
  3273.         else if (c === ';') {
  3274.           start = i + 1;
  3275.         }
  3276.         else if (c === ' ') {
  3277.           start = i + 1;
  3278.         }
  3279.         else if (OPEN_BODY.indexOf(c) != -1) {
  3280.           bodyStack.push({
  3281.             token: c,
  3282.             start: start
  3283.           });
  3284.           start = i + 1;
  3285.         }
  3286.         else if (CLOSE_BODY.indexOf(c) != -1) {
  3287.           var last = bodyStack.pop();
  3288.           if (!last || OPEN_CLOSE_BODY[last.token] != c) {
  3289.             return { err: l10n.lookup('jstypeBeginSyntax') };
  3290.           }
  3291.           if (c === '}') {
  3292.             start = i + 1;
  3293.           }
  3294.           else {
  3295.             start = last.start;
  3296.           }
  3297.         }
  3298.         break;
  3299.  
  3300.       // Double quote state > " <
  3301.       case ParseState.DQUOTE:
  3302.         if (c === '\\') {
  3303.           i ++;
  3304.         }
  3305.         else if (c === '\n') {
  3306.           return { err: l10n.lookup('jstypeBeginUnterm') };
  3307.         }
  3308.         else if (c === '"') {
  3309.           state = ParseState.NORMAL;
  3310.         }
  3311.         break;
  3312.  
  3313.       // Single quote state > ' <
  3314.       case ParseState.QUOTE:
  3315.         if (c === '\\') {
  3316.           i ++;
  3317.         }
  3318.         else if (c === '\n') {
  3319.           return { err: l10n.lookup('jstypeBeginUnterm') };
  3320.         }
  3321.         else if (c === '\'') {
  3322.           state = ParseState.NORMAL;
  3323.         }
  3324.         break;
  3325.     }
  3326.   }
  3327.  
  3328.   return {
  3329.     state: state,
  3330.     startPos: start
  3331.   };
  3332. };
  3333.  
  3334. /**
  3335.  * Return true if the passed object is either an iterator or a generator, and
  3336.  * false otherwise
  3337.  * @param obj The object to check
  3338.  */
  3339. JavascriptType.prototype._isIteratorOrGenerator = function(obj) {
  3340.   if (obj === null) {
  3341.     return false;
  3342.   }
  3343.  
  3344.   if (typeof aObject === 'object') {
  3345.     if (typeof obj.__iterator__ === 'function' ||
  3346.         obj.constructor && obj.constructor.name === 'Iterator') {
  3347.       return true;
  3348.     }
  3349.  
  3350.     try {
  3351.       var str = obj.toString();
  3352.       if (typeof obj.next === 'function' &&
  3353.           str.indexOf('[object Generator') === 0) {
  3354.         return true;
  3355.       }
  3356.     }
  3357.     catch (ex) {
  3358.       // window.history.next throws in the typeof check above.
  3359.       return false;
  3360.     }
  3361.   }
  3362.  
  3363.   return false;
  3364. };
  3365.  
  3366. /**
  3367.  * Would calling 'scope[prop]' cause the invocation of a non-native (i.e. user
  3368.  * defined) function property?
  3369.  * Since calling functions can have side effects, it's only safe to do that if
  3370.  * explicitly requested, rather than because we're trying things out for the
  3371.  * purposes of completion.
  3372.  */
  3373. JavascriptType.prototype._isSafeProperty = function(scope, prop) {
  3374.   if (typeof scope !== 'object') {
  3375.     return false;
  3376.   }
  3377.  
  3378.   // Walk up the prototype chain of 'scope' looking for a property descriptor
  3379.   // for 'prop'
  3380.   var propDesc;
  3381.   while (scope) {
  3382.     try {
  3383.       propDesc = Object.getOwnPropertyDescriptor(scope, prop);
  3384.       if (propDesc) {
  3385.         break;
  3386.       }
  3387.     }
  3388.     catch (ex) {
  3389.       // Native getters throw here. See bug 520882.
  3390.       if (ex.name === 'NS_ERROR_XPC_BAD_CONVERT_JS' ||
  3391.           ex.name === 'NS_ERROR_XPC_BAD_OP_ON_WN_PROTO') {
  3392.         return false;
  3393.       }
  3394.       return true;
  3395.     }
  3396.     scope = Object.getPrototypeOf(scope);
  3397.   }
  3398.  
  3399.   if (!propDesc) {
  3400.     return false;
  3401.   }
  3402.  
  3403.   if (!propDesc.get) {
  3404.     return false;
  3405.   }
  3406.  
  3407.   // The property is safe if 'get' isn't a function or if the function has a
  3408.   // prototype (in which case it's native)
  3409.   return typeof propDesc.get !== 'function' || 'prototype' in propDesc.get;
  3410. };
  3411.  
  3412. JavascriptType.prototype.name = 'javascript';
  3413.  
  3414. exports.JavascriptType = JavascriptType;
  3415.  
  3416.  
  3417. });
  3418. /*
  3419.  * Copyright 2009-2011 Mozilla Foundation and contributors
  3420.  * Licensed under the New BSD license. See LICENSE.txt or:
  3421.  * http://opensource.org/licenses/BSD-3-Clause
  3422.  */
  3423.  
  3424. define('gcli/types/node', ['require', 'exports', 'module' , 'gcli/host', 'gcli/l10n', 'gcli/types'], function(require, exports, module) {
  3425.  
  3426.  
  3427. var host = require('gcli/host');
  3428. var l10n = require('gcli/l10n');
  3429. var types = require('gcli/types');
  3430. var Type = require('gcli/types').Type;
  3431. var Status = require('gcli/types').Status;
  3432. var Conversion = require('gcli/types').Conversion;
  3433.  
  3434.  
  3435. /**
  3436.  * Registration and de-registration.
  3437.  */
  3438. exports.startup = function() {
  3439.   types.registerType(NodeType);
  3440. };
  3441.  
  3442. exports.shutdown = function() {
  3443.   types.unregisterType(NodeType);
  3444. };
  3445.  
  3446. /**
  3447.  * The object against which we complete, which is usually 'window' if it exists
  3448.  * but could be something else in non-web-content environments.
  3449.  */
  3450. var doc;
  3451. if (typeof document !== 'undefined') {
  3452.   doc = document;
  3453. }
  3454.  
  3455. /**
  3456.  * Setter for the document that contains the nodes we're matching
  3457.  */
  3458. exports.setDocument = function(document) {
  3459.   doc = document;
  3460. };
  3461.  
  3462. /**
  3463.  * Undo the effects of setDocument()
  3464.  */
  3465. exports.unsetDocument = function() {
  3466.   doc = undefined;
  3467. };
  3468.  
  3469.  
  3470. /**
  3471.  * A CSS expression that refers to a single node
  3472.  */
  3473. function NodeType(typeSpec) {
  3474.   if (typeSpec != null) {
  3475.     throw new Error('NodeType can not be customized');
  3476.   }
  3477. }
  3478.  
  3479. NodeType.prototype = Object.create(Type.prototype);
  3480.  
  3481. NodeType.prototype.stringify = function(value) {
  3482.   return value.__gcliQuery || 'Error';
  3483. };
  3484.  
  3485. NodeType.prototype.parse = function(arg) {
  3486.   if (arg.text === '') {
  3487.     return new Conversion(null, arg, Status.INCOMPLETE,
  3488.             l10n.lookup('nodeParseNone'));
  3489.   }
  3490.  
  3491.   var nodes;
  3492.   try {
  3493.     nodes = doc.querySelectorAll(arg.text);
  3494.   }
  3495.   catch (ex) {
  3496.     return new Conversion(null, arg, Status.ERROR,
  3497.             l10n.lookup('nodeParseSyntax'));
  3498.   }
  3499.  
  3500.   if (nodes.length === 0) {
  3501.     return new Conversion(null, arg, Status.INCOMPLETE,
  3502.         l10n.lookup('nodeParseNone'));
  3503.   }
  3504.  
  3505.   if (nodes.length === 1) {
  3506.     var node = nodes.item(0);
  3507.     node.__gcliQuery = arg.text;
  3508.  
  3509.     host.flashNode(node, 'green');
  3510.  
  3511.     return new Conversion(node, arg, Status.VALID, '');
  3512.   }
  3513.  
  3514.   Array.prototype.forEach.call(nodes, function(n) {
  3515.     host.flashNode(n, 'red');
  3516.   });
  3517.  
  3518.   return new Conversion(null, arg, Status.ERROR,
  3519.           l10n.lookupFormat('nodeParseMultiple', [ nodes.length ]));
  3520. };
  3521.  
  3522. NodeType.prototype.name = 'node';
  3523.  
  3524.  
  3525. });
  3526. /*
  3527.  * Copyright 2009-2011 Mozilla Foundation and contributors
  3528.  * Licensed under the New BSD license. See LICENSE.txt or:
  3529.  * http://opensource.org/licenses/BSD-3-Clause
  3530.  */
  3531.  
  3532. define('gcli/host', ['require', 'exports', 'module' ], function(require, exports, module) {
  3533.  
  3534.  
  3535. /**
  3536.  * Helper to turn a node background it's complementary color for 1 second.
  3537.  * There is likely a better way to do this, but this will do for now.
  3538.  */
  3539. exports.flashNode = function(node, color) {
  3540.   if (!node.__gcliHighlighting) {
  3541.     node.__gcliHighlighting = true;
  3542.     var original = node.style.background;
  3543.     node.style.background = color;
  3544.     setTimeout(function() {
  3545.       node.style.background = original;
  3546.       delete node.__gcliHighlighting;
  3547.     }, 1000);
  3548.   }
  3549. };
  3550.  
  3551.  
  3552. });
  3553. /*
  3554.  * Copyright 2009-2011 Mozilla Foundation and contributors
  3555.  * Licensed under the New BSD license. See LICENSE.txt or:
  3556.  * http://opensource.org/licenses/BSD-3-Clause
  3557.  */
  3558.  
  3559. define('gcli/cli', ['require', 'exports', 'module' , 'gcli/util', 'gcli/canon', 'gcli/promise', 'gcli/types', 'gcli/types/basic', 'gcli/argument'], function(require, exports, module) {
  3560.  
  3561.  
  3562. var util = require('gcli/util');
  3563.  
  3564. var canon = require('gcli/canon');
  3565. var Promise = require('gcli/promise').Promise;
  3566.  
  3567. var types = require('gcli/types');
  3568. var Status = require('gcli/types').Status;
  3569. var Conversion = require('gcli/types').Conversion;
  3570. var Type = require('gcli/types').Type;
  3571. var ArrayType = require('gcli/types/basic').ArrayType;
  3572. var StringType = require('gcli/types/basic').StringType;
  3573. var BooleanType = require('gcli/types/basic').BooleanType;
  3574. var SelectionType = require('gcli/types/basic').SelectionType;
  3575.  
  3576. var Argument = require('gcli/argument').Argument;
  3577. var ArrayArgument = require('gcli/argument').ArrayArgument;
  3578. var NamedArgument = require('gcli/argument').NamedArgument;
  3579. var TrueNamedArgument = require('gcli/argument').TrueNamedArgument;
  3580. var MergedArgument = require('gcli/argument').MergedArgument;
  3581. var ScriptArgument = require('gcli/argument').ScriptArgument;
  3582.  
  3583. var evalCommand;
  3584.  
  3585. /**
  3586.  * Registration and de-registration.
  3587.  */
  3588. exports.startup = function() {
  3589.   types.registerType(CommandType);
  3590.   evalCommand = canon.addCommand(evalCommandSpec);
  3591. };
  3592.  
  3593. exports.shutdown = function() {
  3594.   types.unregisterType(CommandType);
  3595.   canon.removeCommand(evalCommandSpec.name);
  3596.   evalCommand = undefined;
  3597. };
  3598.  
  3599.  
  3600. /**
  3601.  * Assignment is a link between a parameter and the data for that parameter.
  3602.  * The data for the parameter is available as in the preferred type and as
  3603.  * an Argument for the CLI.
  3604.  * <p>We also record validity information where applicable.
  3605.  * <p>For values, null and undefined have distinct definitions. null means
  3606.  * that a value has been provided, undefined means that it has not.
  3607.  * Thus, null is a valid default value, and common because it identifies an
  3608.  * parameter that is optional. undefined means there is no value from
  3609.  * the command line.
  3610.  *
  3611.  * <h2>Events<h2>
  3612.  * Assignment publishes the following event:<ul>
  3613.  * <li>assignmentChange: Either the value or the text has changed. It is likely
  3614.  * that any UI component displaying this argument will need to be updated.
  3615.  * The event object looks like:
  3616.  * <tt>{ assignment: ..., conversion: ..., oldConversion: ... }</tt>
  3617.  * @constructor
  3618.  */
  3619. function Assignment(param, paramIndex) {
  3620.   this.param = param;
  3621.   this.paramIndex = paramIndex;
  3622.   this.assignmentChange = util.createEvent('Assignment.assignmentChange');
  3623.  
  3624.   this.setDefault();
  3625. }
  3626.  
  3627. /**
  3628.  * The parameter that we are assigning to
  3629.  * @readonly
  3630.  */
  3631. Assignment.prototype.param = undefined;
  3632.  
  3633. Assignment.prototype.conversion = undefined;
  3634.  
  3635. /**
  3636.  * The index of this parameter in the parent Requisition. paramIndex === -1
  3637.  * is the command assignment although this should not be relied upon, it is
  3638.  * better to test param instanceof CommandAssignment
  3639.  */
  3640. Assignment.prototype.paramIndex = undefined;
  3641.  
  3642. /**
  3643.  * Easy accessor for conversion.arg
  3644.  */
  3645. Assignment.prototype.getArg = function() {
  3646.   return this.conversion.arg;
  3647. };
  3648.  
  3649. /**
  3650.  * Easy accessor for conversion.value
  3651.  */
  3652. Assignment.prototype.getValue = function() {
  3653.   return this.conversion.value;
  3654. };
  3655.  
  3656. /**
  3657.  * Easy (and safe) accessor for conversion.message
  3658.  */
  3659. Assignment.prototype.getMessage = function() {
  3660.   return this.conversion.message ? this.conversion.message : '';
  3661. };
  3662.  
  3663. /**
  3664.  * Easy (and safe) accessor for conversion.getPredictions()
  3665.  * @return An array of objects with name and value elements. For example:
  3666.  * [ { name:'bestmatch', value:foo1 }, { name:'next', value:foo2 }, ... ]
  3667.  */
  3668. Assignment.prototype.getPredictions = function() {
  3669.   return this.conversion.getPredictions();
  3670. };
  3671.  
  3672. /**
  3673.  * Report on the status of the last parse() conversion.
  3674.  * We force mutations to happen through this method rather than have
  3675.  * setValue and setArgument functions to help maintain integrity when we
  3676.  * have ArrayArguments and don't want to get confused. This way assignments
  3677.  * are just containers for a conversion rather than things that store
  3678.  * a connection between an arg/value.
  3679.  * @see types.Conversion
  3680.  */
  3681. Assignment.prototype.setConversion = function(conversion) {
  3682.   var oldConversion = this.conversion;
  3683.  
  3684.   this.conversion = conversion;
  3685.   this.conversion.assign(this);
  3686.  
  3687.   if (this.conversion.equals(oldConversion)) {
  3688.     return;
  3689.   }
  3690.  
  3691.   this.assignmentChange({
  3692.     assignment: this,
  3693.     conversion: this.conversion,
  3694.     oldConversion: oldConversion
  3695.   });
  3696. };
  3697.  
  3698. /**
  3699.  * Find a default value for the conversion either from the parameter, or from
  3700.  * the type, or failing that by parsing an empty argument.
  3701.  */
  3702. Assignment.prototype.setDefault = function() {
  3703.   var conversion;
  3704.   if (this.param.getDefault) {
  3705.     conversion = this.param.getDefault();
  3706.   }
  3707.   else if (this.param.type.getDefault) {
  3708.     conversion = this.param.type.getDefault();
  3709.   }
  3710.   else {
  3711.     conversion = this.param.type.parse(new Argument());
  3712.   }
  3713.  
  3714.   this.setConversion(conversion);
  3715. };
  3716.  
  3717. /**
  3718.  * Make sure that there is some content for this argument by using an
  3719.  * Argument of '' if needed.
  3720.  */
  3721. Assignment.prototype.ensureVisibleArgument = function() {
  3722.   // It isn't clear if we should be sending events from this method.
  3723.   // It should only be called when structural changes are happening in which
  3724.   // case we're going to ignore the event anyway. But on the other hand
  3725.   // perhaps this function shouldn't need to know how it is used, and should
  3726.   // do the inefficient thing.
  3727.   if (!this.conversion.arg.isBlank()) {
  3728.     return false;
  3729.   }
  3730.  
  3731.   var arg = this.conversion.arg.beget('', {
  3732.     prefixSpace: this.param instanceof CommandAssignment
  3733.   });
  3734.   this.conversion = this.param.type.parse(arg);
  3735.   this.conversion.assign(this);
  3736.  
  3737.   return true;
  3738. };
  3739.  
  3740. /**
  3741.  * Work out what the status of the current conversion is which involves looking
  3742.  * not only at the conversion, but also checking if data has been provided
  3743.  * where it should.
  3744.  * @param arg For assignments with multiple args (e.g. array assignments) we
  3745.  * can narrow the search for status to a single argument.
  3746.  */
  3747. Assignment.prototype.getStatus = function(arg) {
  3748.   if (this.param.isDataRequired() && !this.conversion.isDataProvided()) {
  3749.     return Status.ERROR;
  3750.   }
  3751.  
  3752.   // Selection/Boolean types with a defined range of values will say that
  3753.   // '' is INCOMPLETE, but the parameter may be optional, so we don't ask
  3754.   // if the user doesn't need to enter something and hasn't done so.
  3755.   if (!this.param.isDataRequired() && this.getArg().isBlank()) {
  3756.     return Status.VALID;
  3757.   }
  3758.  
  3759.   return this.conversion.getStatus(arg);
  3760. };
  3761.  
  3762. /**
  3763.  * Basically <tt>value = conversion.predictions[0])</tt> done in a safe way.
  3764.  */
  3765. Assignment.prototype.complete = function() {
  3766.   var predictions = this.conversion.getPredictions();
  3767.   if (predictions.length > 0) {
  3768.     var arg = this.conversion.arg.beget(predictions[0].name);
  3769.     if (!predictions[0].incomplete) {
  3770.       arg.suffix = ' ';
  3771.     }
  3772.     var conversion = this.param.type.parse(arg);
  3773.     this.setConversion(conversion);
  3774.   }
  3775. };
  3776.  
  3777. /**
  3778.  * Replace the current value with the lower value if such a concept exists.
  3779.  */
  3780. Assignment.prototype.decrement = function() {
  3781.   var replacement = this.param.type.decrement(this.conversion.value);
  3782.   if (replacement != null) {
  3783.     var str = this.param.type.stringify(replacement);
  3784.     var arg = this.conversion.arg.beget(str);
  3785.     var conversion = new Conversion(replacement, arg);
  3786.     this.setConversion(conversion);
  3787.   }
  3788. };
  3789.  
  3790. /**
  3791.  * Replace the current value with the higher value if such a concept exists.
  3792.  */
  3793. Assignment.prototype.increment = function() {
  3794.   var replacement = this.param.type.increment(this.conversion.value);
  3795.   if (replacement != null) {
  3796.     var str = this.param.type.stringify(replacement);
  3797.     var arg = this.conversion.arg.beget(str);
  3798.     var conversion = new Conversion(replacement, arg);
  3799.     this.setConversion(conversion);
  3800.   }
  3801. };
  3802.  
  3803. /**
  3804.  * Helper when we're rebuilding command lines.
  3805.  */
  3806. Assignment.prototype.toString = function() {
  3807.   return this.conversion.toString();
  3808. };
  3809.  
  3810. exports.Assignment = Assignment;
  3811.  
  3812.  
  3813. /**
  3814.  * Select from the available commands.
  3815.  * This is very similar to a SelectionType, however the level of hackery in
  3816.  * SelectionType to make it handle Commands correctly was to high, so we
  3817.  * simplified.
  3818.  * If you are making changes to this code, you should check there too.
  3819.  */
  3820. function CommandType() {
  3821. }
  3822.  
  3823. CommandType.prototype = Object.create(Type.prototype);
  3824.  
  3825. CommandType.prototype.name = 'command';
  3826.  
  3827. CommandType.prototype.decrement = SelectionType.prototype.decrement;
  3828. CommandType.prototype.increment = SelectionType.prototype.increment;
  3829. CommandType.prototype._findValue = SelectionType.prototype._findValue;
  3830.  
  3831. CommandType.prototype.stringify = function(command) {
  3832.   return command.name;
  3833. };
  3834.  
  3835. /**
  3836.  * Trim a list of commands (as from canon.getCommands()) according to those
  3837.  * that match the provided arg.
  3838.  */
  3839. CommandType.prototype._findPredictions = function(arg) {
  3840.   var predictions = [];
  3841.   canon.getCommands().forEach(function(command) {
  3842.     if (command.name.indexOf(arg.text) === 0) {
  3843.       // The command type needs to exclude sub-commands when the CLI
  3844.       // is blank, but include them when we're filtering. This hack
  3845.       // excludes matches when the filter text is '' and when the
  3846.       // name includes a space.
  3847.       if (arg.text.length !== 0 || command.name.indexOf(' ') === -1) {
  3848.         predictions.push(command);
  3849.       }
  3850.     }
  3851.   }, this);
  3852.   return predictions;
  3853. };
  3854.  
  3855. CommandType.prototype.parse = function(arg) {
  3856.   // Especially at startup, predictions live over the time that things change
  3857.   // so we provide a completion function rather than completion values
  3858.   var predictFunc = function() {
  3859.     return this._findPredictions(arg);
  3860.   }.bind(this);
  3861.  
  3862.   var predictions = this._findPredictions(arg);
  3863.  
  3864.   if (predictions.length === 0) {
  3865.     return new Conversion(null, arg, Status.ERROR,
  3866.         'Can\'t use \'' + arg.text + '\'.', predictFunc);
  3867.   }
  3868.  
  3869.   var command = predictions[0];
  3870.  
  3871.   if (predictions.length === 1) {
  3872.     // Is it an exact match of an executable command,
  3873.     // or just the only possibility?
  3874.     if (command.name === arg.text && typeof command.exec === 'function') {
  3875.       return new Conversion(command, arg, Status.VALID, '');
  3876.     }
  3877.     return new Conversion(null, arg, Status.INCOMPLETE, '', predictFunc);
  3878.   }
  3879.  
  3880.   // It's valid if the text matches, even if there are several options
  3881.   if (command.name === arg.text) {
  3882.     return new Conversion(command, arg, Status.VALID, '', predictFunc);
  3883.   }
  3884.  
  3885.   return new Conversion(null, arg, Status.INCOMPLETE, '', predictFunc);
  3886. };
  3887.  
  3888.  
  3889. /**
  3890.  * How to dynamically execute JavaScript code
  3891.  */
  3892. var customEval = eval;
  3893.  
  3894. /**
  3895.  * Setup a function to be called in place of 'eval', generally for security
  3896.  * reasons
  3897.  */
  3898. exports.setEvalFunction = function(newCustomEval) {
  3899.   customEval = newCustomEval;
  3900. };
  3901.  
  3902. /**
  3903.  * Remove the binding done by setEvalFunction().
  3904.  * We purposely set customEval to undefined rather than to 'eval' because there
  3905.  * is an implication of setEvalFunction that we're in a security sensitive
  3906.  * situation. What if we can trick GCLI into calling unsetEvalFunction() at the
  3907.  * wrong time?
  3908.  * So to properly undo the effects of setEvalFunction(), you need to call
  3909.  * setEvalFunction(eval) rather than unsetEvalFunction(), however the latter is
  3910.  * preferred in most cases.
  3911.  */
  3912. exports.unsetEvalFunction = function() {
  3913.   customEval = undefined;
  3914. };
  3915.  
  3916. /**
  3917.  * 'eval' command
  3918.  */
  3919. var evalCommandSpec = {
  3920.   name: '{',
  3921.   params: [
  3922.     {
  3923.       name: 'javascript',
  3924.       type: 'javascript',
  3925.       description: ''
  3926.     }
  3927.   ],
  3928.   returnType: 'html',
  3929.   description: { key: 'cliEvalJavascript' },
  3930.   exec: function(args, context) {
  3931.     // → is right arrow. We use explicit entities to ensure XML validity
  3932.     var resultPrefix = '<em>{ ' + args.javascript + ' }</em> → ';
  3933.     try {
  3934.       var result = customEval(args.javascript);
  3935.  
  3936.       if (result === null) {
  3937.         return resultPrefix + 'null.';
  3938.       }
  3939.  
  3940.       if (result === undefined) {
  3941.         return resultPrefix + 'undefined.';
  3942.       }
  3943.  
  3944.       if (typeof result === 'function') {
  3945.         //   is  
  3946.         return resultPrefix +
  3947.             (result + '').replace(/\n/g, '<br>').replace(/ /g, ' ');
  3948.       }
  3949.  
  3950.       return resultPrefix + result;
  3951.     }
  3952.     catch (ex) {
  3953.       return resultPrefix + 'Exception: ' + ex.message;
  3954.     }
  3955.   }
  3956. };
  3957.  
  3958.  
  3959. /**
  3960.  * This is a special assignment to reflect the command itself.
  3961.  */
  3962. function CommandAssignment() {
  3963.   this.param = new canon.Parameter({
  3964.     name: '__command',
  3965.     type: 'command',
  3966.     description: 'The command to execute'
  3967.   });
  3968.   this.paramIndex = -1;
  3969.   this.assignmentChange = util.createEvent('CommandAssignment.assignmentChange');
  3970.  
  3971.   this.setDefault();
  3972. }
  3973.  
  3974. CommandAssignment.prototype = Object.create(Assignment.prototype);
  3975.  
  3976. CommandAssignment.prototype.getStatus = function(arg) {
  3977.   return Status.combine(
  3978.     Assignment.prototype.getStatus.call(this, arg),
  3979.     this.conversion.value && !this.conversion.value.exec ?
  3980.       Status.INCOMPLETE : Status.VALID
  3981.   );
  3982. };
  3983.  
  3984.  
  3985. /**
  3986.  * Special assignment used when ignoring parameters that don't have a home
  3987.  */
  3988. function UnassignedAssignment() {
  3989.   this.param = new canon.Parameter({
  3990.     name: '__unassigned',
  3991.     type: 'string'
  3992.   });
  3993.   this.paramIndex = -1;
  3994.   this.assignmentChange = util.createEvent('UnassignedAssignment.assignmentChange');
  3995.  
  3996.   this.setDefault();
  3997. }
  3998.  
  3999. UnassignedAssignment.prototype = Object.create(Assignment.prototype);
  4000.  
  4001. UnassignedAssignment.prototype.getStatus = function(arg) {
  4002.   return Status.ERROR;
  4003. };
  4004.  
  4005. UnassignedAssignment.prototype.setUnassigned = function(args) {
  4006.   if (!args || args.length === 0) {
  4007.     this.setDefault();
  4008.   }
  4009.   else {
  4010.     var conversion = this.param.type.parse(new MergedArgument(args));
  4011.     this.setConversion(conversion);
  4012.   }
  4013. };
  4014.  
  4015.  
  4016. /**
  4017.  * A Requisition collects the information needed to execute a command.
  4018.  *
  4019.  * (For a definition of the term, see http://en.wikipedia.org/wiki/Requisition)
  4020.  * This term is used because carries the notion of a work-flow, or process to
  4021.  * getting the information to execute a command correct.
  4022.  * There is little point in a requisition for parameter-less commands because
  4023.  * there is no information to collect. A Requisition is a collection of
  4024.  * assignments of values to parameters, each handled by an instance of
  4025.  * Assignment.
  4026.  *
  4027.  * <h2>Events<h2>
  4028.  * <p>Requisition publishes the following events:
  4029.  * <ul>
  4030.  * <li>commandChange: The command has changed. It is likely that a UI
  4031.  * structure will need updating to match the parameters of the new command.
  4032.  * The event object looks like { command: A }
  4033.  * <li>assignmentChange: This is a forward of the Assignment.assignmentChange
  4034.  * event. It is fired when any assignment (except the commandAssignment)
  4035.  * changes.
  4036.  * <li>inputChange: The text to be mirrored in a command line has changed.
  4037.  * The event object looks like { newText: X }.
  4038.  * </ul>
  4039.  *
  4040.  * @param environment An opaque object passed to commands using ExecutionContext
  4041.  * @param document A DOM Document passed to commands using ExecutionContext in
  4042.  * order to allow creation of DOM nodes.
  4043.  * @constructor
  4044.  */
  4045. function Requisition(environment, document) {
  4046.   this.environment = environment;
  4047.   this.document = document;
  4048.  
  4049.   // The command that we are about to execute.
  4050.   // @see setCommandConversion()
  4051.   this.commandAssignment = new CommandAssignment();
  4052.  
  4053.   // The object that stores of Assignment objects that we are filling out.
  4054.   // The Assignment objects are stored under their param.name for named
  4055.   // lookup. Note: We make use of the property of Javascript objects that
  4056.   // they are not just hashmaps, but linked-list hashmaps which iterate in
  4057.   // insertion order.
  4058.   // _assignments excludes the commandAssignment.
  4059.   this._assignments = {};
  4060.  
  4061.   // The count of assignments. Excludes the commandAssignment
  4062.   this.assignmentCount = 0;
  4063.  
  4064.   // Used to store cli arguments in the order entered on the cli
  4065.   this._args = [];
  4066.  
  4067.   // Used to store cli arguments that were not assigned to parameters
  4068.   this._unassigned = new UnassignedAssignment();
  4069.  
  4070.   // Temporarily set this to true to prevent _onAssignmentChange resetting
  4071.   // argument positions
  4072.   this._structuralChangeInProgress = false;
  4073.  
  4074.   this.commandAssignment.assignmentChange.add(this._onCommandAssignmentChange, this);
  4075.   this.commandAssignment.assignmentChange.add(this._onAssignmentChange, this);
  4076.  
  4077.   this.commandOutputManager = canon.commandOutputManager;
  4078.  
  4079.   this.assignmentChange = util.createEvent('Requisition.assignmentChange');
  4080.   this.commandChange = util.createEvent('Requisition.commandChange');
  4081.   this.inputChange = util.createEvent('Requisition.inputChange');
  4082. }
  4083.  
  4084. /**
  4085.  * Some number that is higher than the most args we'll ever have. Would use
  4086.  * MAX_INTEGER if that made sense
  4087.  */
  4088. var MORE_THAN_THE_MOST_ARGS_POSSIBLE = 1000000;
  4089.  
  4090. /**
  4091.  * Avoid memory leaks
  4092.  */
  4093. Requisition.prototype.destroy = function() {
  4094.   this.commandAssignment.assignmentChange.remove(this._onCommandAssignmentChange, this);
  4095.   this.commandAssignment.assignmentChange.remove(this._onAssignmentChange, this);
  4096.  
  4097.   delete this.document;
  4098.   delete this.environment;
  4099. };
  4100.  
  4101. /**
  4102.  * When any assignment changes, we might need to update the _args array to
  4103.  * match and inform people of changes to the typed input text.
  4104.  */
  4105. Requisition.prototype._onAssignmentChange = function(ev) {
  4106.   // Don't report an event if the value is unchanged
  4107.   if (ev.oldConversion != null &&
  4108.       ev.conversion.valueEquals(ev.oldConversion)) {
  4109.     return;
  4110.   }
  4111.  
  4112.   if (this._structuralChangeInProgress) {
  4113.     return;
  4114.   }
  4115.  
  4116.   this.assignmentChange(ev);
  4117.  
  4118.   // Both for argument position and the inputChange event, we only care
  4119.   // about changes to the argument.
  4120.   if (ev.conversion.argEquals(ev.oldConversion)) {
  4121.     return;
  4122.   }
  4123.  
  4124.   this._structuralChangeInProgress = true;
  4125.  
  4126.   // Refactor? See bug 660765
  4127.   // Do preceding arguments need to have dummy values applied so we don't
  4128.   // get a hole in the command line?
  4129.   if (ev.assignment.param.isPositionalAllowed()) {
  4130.     for (var i = 0; i < ev.assignment.paramIndex; i++) {
  4131.       var assignment = this.getAssignment(i);
  4132.       if (assignment.param.isPositionalAllowed()) {
  4133.         if (assignment.ensureVisibleArgument()) {
  4134.           this._args.push(assignment.getArg());
  4135.         }
  4136.       }
  4137.     }
  4138.   }
  4139.  
  4140.   // Remember where we found the first match
  4141.   var index = MORE_THAN_THE_MOST_ARGS_POSSIBLE;
  4142.   for (var i = 0; i < this._args.length; i++) {
  4143.     if (this._args[i].assignment === ev.assignment) {
  4144.       if (i < index) {
  4145.         index = i;
  4146.       }
  4147.       this._args.splice(i, 1);
  4148.       i--;
  4149.     }
  4150.   }
  4151.  
  4152.   if (index === MORE_THAN_THE_MOST_ARGS_POSSIBLE) {
  4153.     this._args.push(ev.assignment.getArg());
  4154.   }
  4155.   else {
  4156.     // Is there a way to do this that doesn't involve a loop?
  4157.     var newArgs = ev.conversion.arg.getArgs();
  4158.     for (var i = 0; i < newArgs.length; i++) {
  4159.       this._args.splice(index + i, 0, newArgs[i]);
  4160.     }
  4161.   }
  4162.   this._structuralChangeInProgress = false;
  4163.  
  4164.   this.inputChange();
  4165. };
  4166.  
  4167. /**
  4168.  * When the command changes, we need to keep a bunch of stuff in sync
  4169.  */
  4170. Requisition.prototype._onCommandAssignmentChange = function(ev) {
  4171.   this._assignments = {};
  4172.  
  4173.   var command = this.commandAssignment.getValue();
  4174.   if (command) {
  4175.     for (var i = 0; i < command.params.length; i++) {
  4176.       var param = command.params[i];
  4177.       var assignment = new Assignment(param, i);
  4178.       assignment.assignmentChange.add(this._onAssignmentChange, this);
  4179.       this._assignments[param.name] = assignment;
  4180.     }
  4181.   }
  4182.   this.assignmentCount = Object.keys(this._assignments).length;
  4183.  
  4184.   this.commandChange({
  4185.     requisition: this,
  4186.     oldValue: ev.oldValue,
  4187.     newValue: command
  4188.   });
  4189. //  this.inputChange();
  4190. };
  4191.  
  4192. /**
  4193.  * Assignments have an order, so we need to store them in an array.
  4194.  * But we also need named access ...
  4195.  * @return The found assignment, or undefined, if no match was found
  4196.  */
  4197. Requisition.prototype.getAssignment = function(nameOrNumber) {
  4198.   var name = (typeof nameOrNumber === 'string') ?
  4199.     nameOrNumber :
  4200.     Object.keys(this._assignments)[nameOrNumber];
  4201.   return this._assignments[name] || undefined;
  4202. },
  4203.  
  4204. /**
  4205.  * Where parameter name == assignment names - they are the same
  4206.  */
  4207. Requisition.prototype.getParameterNames = function() {
  4208.   return Object.keys(this._assignments);
  4209. },
  4210.  
  4211. /**
  4212.  * A *shallow* clone of the assignments.
  4213.  * This is useful for systems that wish to go over all the assignments
  4214.  * finding values one way or another and wish to trim an array as they go.
  4215.  */
  4216. Requisition.prototype.cloneAssignments = function() {
  4217.   return Object.keys(this._assignments).map(function(name) {
  4218.     return this._assignments[name];
  4219.   }, this);
  4220. };
  4221.  
  4222. /**
  4223.  * The overall status is the most severe status.
  4224.  * There is no such thing as an INCOMPLETE overall status because the
  4225.  * definition of INCOMPLETE takes into account the cursor position to say 'this
  4226.  * isn't quite ERROR because the user can fix it by typing', however overall,
  4227.  * this is still an error status.
  4228.  */
  4229. Requisition.prototype.getStatus = function() {
  4230.   var status = Status.VALID;
  4231.   this.getAssignments(true).forEach(function(assignment) {
  4232.     var assignStatus = assignment.getStatus();
  4233.     if (assignment.getStatus() > status) {
  4234.       status = assignStatus;
  4235.     }
  4236.   }, this);
  4237.   if (status === Status.INCOMPLETE) {
  4238.     status = Status.ERROR;
  4239.   }
  4240.   return status;
  4241. };
  4242.  
  4243. /**
  4244.  * Extract the names and values of all the assignments, and return as
  4245.  * an object.
  4246.  */
  4247. Requisition.prototype.getArgsObject = function() {
  4248.   var args = {};
  4249.   this.getAssignments().forEach(function(assignment) {
  4250.     args[assignment.param.name] = assignment.getValue();
  4251.   }, this);
  4252.   return args;
  4253. };
  4254.  
  4255. /**
  4256.  * Access the arguments as an array.
  4257.  * @param includeCommand By default only the parameter arguments are
  4258.  * returned unless (includeCommand === true), in which case the list is
  4259.  * prepended with commandAssignment.getArg()
  4260.  */
  4261. Requisition.prototype.getAssignments = function(includeCommand) {
  4262.   var assignments = [];
  4263.   if (includeCommand === true) {
  4264.     assignments.push(this.commandAssignment);
  4265.   }
  4266.   Object.keys(this._assignments).forEach(function(name) {
  4267.     assignments.push(this.getAssignment(name));
  4268.   }, this);
  4269.   return assignments;
  4270. };
  4271.  
  4272. /**
  4273.  * Reset all the assignments to their default values
  4274.  */
  4275. Requisition.prototype.setDefaultArguments = function() {
  4276.   this.getAssignments().forEach(function(assignment) {
  4277.     assignment.setDefault();
  4278.   }, this);
  4279. };
  4280.  
  4281. /**
  4282.  * Extract a canonical version of the input
  4283.  */
  4284. Requisition.prototype.toCanonicalString = function() {
  4285.   var line = [];
  4286.  
  4287.   var cmd = this.commandAssignment.getValue() ?
  4288.       this.commandAssignment.getValue().name :
  4289.       this.commandAssignment.getArg().text;
  4290.   line.push(cmd);
  4291.  
  4292.   Object.keys(this._assignments).forEach(function(name) {
  4293.     var assignment = this._assignments[name];
  4294.     var type = assignment.param.type;
  4295.     // Bug 664377: This will cause problems if there is a non-default value
  4296.     // after a default value. Also we need to decide when to use
  4297.     // named parameters in place of positional params. Both can wait.
  4298.     if (assignment.getValue() !== assignment.param.defaultValue) {
  4299.       line.push(' ');
  4300.       line.push(type.stringify(assignment.getValue()));
  4301.     }
  4302.   }, this);
  4303.  
  4304.   // Canonically, if we've opened with a { then we should have a } to close
  4305.   var command = this.commandAssignment.getValue();
  4306.   if (cmd === '{') {
  4307.     if (this.getAssignment(0).getArg().suffix.indexOf('}') === -1) {
  4308.       line.push(' }');
  4309.     }
  4310.   }
  4311.  
  4312.   return line.join('');
  4313. };
  4314.  
  4315. /**
  4316.  * Input trace gives us an array of Argument tracing objects, one for each
  4317.  * character in the typed input, from which we can derive information about how
  4318.  * to display this typed input. It's a bit like toString on steroids.
  4319.  * <p>
  4320.  * The returned object has the following members:<ul>
  4321.  * <li>char: The character to which this arg trace refers.
  4322.  * <li>arg: The Argument to which this character is assigned.
  4323.  * <li>part: One of ['prefix'|'text'|suffix'] - how was this char understood
  4324.  * </ul>
  4325.  * <p>
  4326.  * The Argument objects are as output from #_tokenize() rather than as applied
  4327.  * to Assignments by #_assign() (i.e. they are not instances of NamedArgument,
  4328.  * ArrayArgument, etc).
  4329.  * <p>
  4330.  * To get at the arguments applied to the assignments simply call
  4331.  * <tt>arg.assignment.arg</tt>. If <tt>arg.assignment.arg !== arg</tt> then
  4332.  * the arg applied to the assignment will contain the original arg.
  4333.  * See #_assign() for details.
  4334.  */
  4335. Requisition.prototype.createInputArgTrace = function() {
  4336.   if (!this._args) {
  4337.     throw new Error('createInputMap requires a command line. See source.');
  4338.     // If this is a problem then we can fake command line input using
  4339.     // something like the code in #toCanonicalString().
  4340.   }
  4341.  
  4342.   var args = [];
  4343.   this._args.forEach(function(arg) {
  4344.     for (var i = 0; i < arg.prefix.length; i++) {
  4345.       args.push({ arg: arg, char: arg.prefix[i], part: 'prefix' });
  4346.     }
  4347.     for (var i = 0; i < arg.text.length; i++) {
  4348.       args.push({ arg: arg, char: arg.text[i], part: 'text' });
  4349.     }
  4350.     for (var i = 0; i < arg.suffix.length; i++) {
  4351.       args.push({ arg: arg, char: arg.suffix[i], part: 'suffix' });
  4352.     }
  4353.   });
  4354.  
  4355.   return args;
  4356. };
  4357.  
  4358. /**
  4359.  * Reconstitute the input from the args
  4360.  */
  4361. Requisition.prototype.toString = function() {
  4362.   if (this._args) {
  4363.     return this._args.map(function(arg) {
  4364.       return arg.toString();
  4365.     }).join('');
  4366.   }
  4367.  
  4368.   return this.toCanonicalString();
  4369. };
  4370.  
  4371. /**
  4372.  * Return an array of Status scores so we can create a marked up
  4373.  * version of the command line input.
  4374.  */
  4375. Requisition.prototype.getInputStatusMarkup = function() {
  4376.   var argTraces = this.createInputArgTrace();
  4377.   // We only take a status of INCOMPLETE to be INCOMPLETE when the cursor is
  4378.   // actually in the argument. Otherwise it's an error.
  4379.   // Generally the 'argument at the cursor' is the argument before the cursor
  4380.   // unless it is before the first char, in which case we take the first.
  4381.   var cursor = this.input.cursor.start === 0 ?
  4382.       0 :
  4383.       this.input.cursor.start - 1;
  4384.   var cTrace = argTraces[cursor];
  4385.  
  4386.   var statuses = [];
  4387.   for (var i = 0; i < argTraces.length; i++) {
  4388.     var argTrace = argTraces[i];
  4389.     var arg = argTrace.arg;
  4390.     var status = Status.VALID;
  4391.     if (argTrace.part === 'text') {
  4392.       status = arg.assignment.getStatus(arg);
  4393.       // Promote INCOMPLETE to ERROR  ...
  4394.       if (status === Status.INCOMPLETE) {
  4395.         // If the cursor is not in a position to be able to complete it
  4396.         if (arg !== cTrace.arg || cTrace.part !== 'text') {
  4397.           // And if we're not in the command
  4398.           if (!(arg.assignment instanceof CommandAssignment)) {
  4399.             status = Status.ERROR;
  4400.           }
  4401.         }
  4402.       }
  4403.     }
  4404.  
  4405.     statuses.push(status);
  4406.   }
  4407.  
  4408.   return statuses;
  4409. };
  4410.  
  4411. /**
  4412.  * Look through the arguments attached to our assignments for the assignment
  4413.  * at the given position.
  4414.  * @param {number} cursor The cursor position to query
  4415.  */
  4416. Requisition.prototype.getAssignmentAt = function(cursor) {
  4417.   if (!this._args) {
  4418.     console.trace();
  4419.     throw new Error('Missing args');
  4420.   }
  4421.  
  4422.   // We short circuit this one because we may have no args, or no args with
  4423.   // any size and the alg below only finds arguments with size.
  4424.   if (cursor === 0) {
  4425.     return this.commandAssignment;
  4426.   }
  4427.  
  4428.   var assignForPos = [];
  4429.   var i, j;
  4430.   for (i = 0; i < this._args.length; i++) {
  4431.     var arg = this._args[i];
  4432.     var assignment = arg.assignment;
  4433.  
  4434.     // prefix and text are clearly part of the argument
  4435.     for (j = 0; j < arg.prefix.length; j++) {
  4436.       assignForPos.push(assignment);
  4437.     }
  4438.     for (j = 0; j < arg.text.length; j++) {
  4439.       assignForPos.push(assignment);
  4440.     }
  4441.  
  4442.     // suffix looks forwards
  4443.     if (this._args.length > i + 1) {
  4444.       // first to the next argument
  4445.       assignment = this._args[i + 1].assignment;
  4446.     }
  4447.     else if (assignment &&
  4448.         assignment.paramIndex + 1 < this.assignmentCount) {
  4449.       // then to the next assignment
  4450.       assignment = this.getAssignment(assignment.paramIndex + 1);
  4451.     }
  4452.  
  4453.     for (j = 0; j < arg.suffix.length; j++) {
  4454.       assignForPos.push(assignment);
  4455.     }
  4456.   }
  4457.  
  4458.   // Possible shortcut, we don't really need to go through all the args
  4459.   // to work out the solution to this
  4460.  
  4461.   var reply = assignForPos[cursor - 1];
  4462.  
  4463.   if (!reply) {
  4464.     throw new Error('Missing assignment.' +
  4465.       ' cursor=' + cursor + ' text.length=' + this.toString().length);
  4466.   }
  4467.  
  4468.   return reply;
  4469. };
  4470.  
  4471. /**
  4472.  * Entry point for keyboard accelerators or anything else that wants to execute
  4473.  * a command.
  4474.  * @param input Object containing data about how to execute the command.
  4475.  * Properties of input include:
  4476.  * - args: Arguments for the command
  4477.  * - typed: The typed command
  4478.  * - visible: Ensure that the output from this command is visible
  4479.  */
  4480. Requisition.prototype.exec = function(input) {
  4481.   var command;
  4482.   var args;
  4483.   var visible = true;
  4484.  
  4485.   if (input) {
  4486.     if (input.args != null) {
  4487.       // Fast track by looking up the command directly since passed args
  4488.       // means there is no command line to parse.
  4489.       command = canon.getCommand(input.typed);
  4490.       if (!command) {
  4491.         console.error('Command not found: ' + command);
  4492.       }
  4493.       args = input.args;
  4494.  
  4495.       // Default visible to false since this is exec is probably the
  4496.       // result of a keyboard shortcut
  4497.       visible = 'visible' in input ? input.visible : false;
  4498.     }
  4499.     else {
  4500.       this.update(input);
  4501.     }
  4502.   }
  4503.  
  4504.   if (!command) {
  4505.     command = this.commandAssignment.getValue();
  4506.     args = this.getArgsObject();
  4507.   }
  4508.  
  4509.   if (!command) {
  4510.     return false;
  4511.   }
  4512.  
  4513.   var outputObject = {
  4514.     command: command,
  4515.     args: args,
  4516.     typed: this.toCanonicalString(),
  4517.     completed: false,
  4518.     start: new Date()
  4519.   };
  4520.  
  4521.   this.commandOutputManager.sendCommandOutput(outputObject);
  4522.  
  4523.   var onComplete = (function(output, error) {
  4524.     if (visible) {
  4525.       outputObject.end = new Date();
  4526.       outputObject.duration = outputObject.end.getTime() - outputObject.start.getTime();
  4527.       outputObject.error = error;
  4528.       outputObject.output = output;
  4529.       outputObject.completed = true;
  4530.       this.commandOutputManager.sendCommandOutput(outputObject);
  4531.     }
  4532.   }).bind(this);
  4533.  
  4534.   try {
  4535.     var context = new ExecutionContext(this.environment, this.document);
  4536.     var reply = command.exec(args, context);
  4537.  
  4538.     if (reply != null && reply.isPromise) {
  4539.       reply.then(
  4540.         function(data) { onComplete(data, false); },
  4541.         function(error) { onComplete(error, true); });
  4542.  
  4543.       // Add progress to our promise and add a handler for it here
  4544.       // See bug 659300
  4545.     }
  4546.     else {
  4547.       onComplete(reply, false);
  4548.     }
  4549.   }
  4550.   catch (ex) {
  4551.     onComplete(ex, true);
  4552.   }
  4553.  
  4554.   this.clear();
  4555.   return true;
  4556. };
  4557.  
  4558. /**
  4559.  * Called by the UI when ever the user interacts with a command line input
  4560.  * @param input A structure that details the state of the input field.
  4561.  * It should look something like: { typed:a, cursor: { start:b, end:c } }
  4562.  * Where a is the contents of the input field, and b and c are the start
  4563.  * and end of the cursor/selection respectively.
  4564.  * <p>The general sequence is:
  4565.  * <ul>
  4566.  * <li>_tokenize(): convert _typed into _parts
  4567.  * <li>_split(): convert _parts into _command and _unparsedArgs
  4568.  * <li>_assign(): convert _unparsedArgs into requisition
  4569.  * </ul>
  4570.  */
  4571. Requisition.prototype.update = function(input) {
  4572.   this.input = input;
  4573.   if (this.input.cursor == null) {
  4574.     this.input.cursor = { start: input.length, end: input.length };
  4575.   }
  4576.  
  4577.   this._structuralChangeInProgress = true;
  4578.  
  4579.   this._args = this._tokenize(input.typed);
  4580.  
  4581.   var args = this._args.slice(0); // i.e. clone
  4582.   this._split(args);
  4583.   this._assign(args);
  4584.  
  4585.   this._structuralChangeInProgress = false;
  4586.  
  4587.   this.inputChange();
  4588. };
  4589.  
  4590. /**
  4591.  * Empty the current buffer, and notify listeners that we're now empty
  4592.  */
  4593. Requisition.prototype.clear = function() {
  4594.   this.update({ typed: '', cursor: { start: 0, end: 0 } });
  4595. };
  4596.  
  4597. /**
  4598.  * Requisition._tokenize() is a state machine. These are the states.
  4599.  */
  4600. var In = {
  4601.   /**
  4602.    * The last character was ' '.
  4603.    * Typing a ' ' character will not change the mode
  4604.    * Typing one of '"{ will change mode to SINGLE_Q, DOUBLE_Q or SCRIPT.
  4605.    * Anything else goes into SIMPLE mode.
  4606.    */
  4607.   WHITESPACE: 1,
  4608.  
  4609.   /**
  4610.    * The last character was part of a parameter.
  4611.    * Typing ' ' returns to WHITESPACE mode. Any other character
  4612.    * (including '"{} which are otherwise special) does not change the mode.
  4613.    */
  4614.   SIMPLE: 2,
  4615.  
  4616.   /**
  4617.    * We're inside single quotes: '
  4618.    * Typing ' returns to WHITESPACE mode. Other characters do not change mode.
  4619.    */
  4620.   SINGLE_Q: 3,
  4621.  
  4622.   /**
  4623.    * We're inside double quotes: "
  4624.    * Typing " returns to WHITESPACE mode. Other characters do not change mode.
  4625.    */
  4626.   DOUBLE_Q: 4,
  4627.  
  4628.   /**
  4629.    * We're inside { and }
  4630.    * Typing } returns to WHITESPACE mode. Other characters do not change mode.
  4631.    * SCRIPT mode is slightly different from other modes in that spaces between
  4632.    * the {/} delimiters and the actual input are not considered significant.
  4633.    * e.g: " x " is a 3 character string, delimited by double quotes, however
  4634.    * { x } is a 1 character JavaScript surrounded by whitespace and {}
  4635.    * delimiters.
  4636.    * In the short term we assume that the JS routines can make sense of the
  4637.    * extra whitespace, however at some stage we may need to move the space into
  4638.    * the Argument prefix/suffix.
  4639.    * Also we don't attempt to handle nested {}. See bug 678961
  4640.    */
  4641.   SCRIPT: 5
  4642. };
  4643.  
  4644. /**
  4645.  * Split up the input taking into account ', " and {.
  4646.  * We don't consider \t or other classical whitespace characters to split
  4647.  * arguments apart. For one thing these characters are hard to type, but also
  4648.  * if the user has gone to the trouble of pasting a TAB character into the
  4649.  * input field (or whatever it takes), they probably mean it.
  4650.  */
  4651. Requisition.prototype._tokenize = function(typed) {
  4652.   // For blank input, place a dummy empty argument into the list
  4653.   if (typed == null || typed.length === 0) {
  4654.     return [ new Argument('', '', '') ];
  4655.   }
  4656.  
  4657.   if (isSimple(typed)) {
  4658.     return [ new Argument(typed, '', '') ];
  4659.   }
  4660.  
  4661.   var mode = In.WHITESPACE;
  4662.  
  4663.   // First we un-escape. This list was taken from:
  4664.   // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Unicode
  4665.   // We are generally converting to their real values except for the strings
  4666.   // '\'', '\"', '\ ', '{' and '}' which we are converting to unicode private
  4667.   // characters so we can distinguish them from '"', ' ', '{', '}' and ''',
  4668.   // which are special. They need swapping back post-split - see unescape2()
  4669.   typed = typed
  4670.       .replace(/\\\\/g, '\\')
  4671.       .replace(/\\b/g, '\b')
  4672.       .replace(/\\f/g, '\f')
  4673.       .replace(/\\n/g, '\n')
  4674.       .replace(/\\r/g, '\r')
  4675.       .replace(/\\t/g, '\t')
  4676.       .replace(/\\v/g, '\v')
  4677.       .replace(/\\n/g, '\n')
  4678.       .replace(/\\r/g, '\r')
  4679.       .replace(/\\ /g, '\uF000')
  4680.       .replace(/\\'/g, '\uF001')
  4681.       .replace(/\\"/g, '\uF002')
  4682.       .replace(/\\{/g, '\uF003')
  4683.       .replace(/\\}/g, '\uF004');
  4684.  
  4685.   function unescape2(escaped) {
  4686.     return escaped
  4687.         .replace(/\uF000/g, ' ')
  4688.         .replace(/\uF001/g, '\'')
  4689.         .replace(/\uF002/g, '"')
  4690.         .replace(/\uF003/g, '{')
  4691.         .replace(/\uF004/g, '}');
  4692.   }
  4693.  
  4694.   var i = 0;          // The index of the current character
  4695.   var start = 0;      // Where did this section start?
  4696.   var prefix = '';    // Stuff that comes before the current argument
  4697.   var args = [];      // The array that we're creating
  4698.   var blockDepth = 0; // For JS with nested {}
  4699.  
  4700.   // This is just a state machine. We're going through the string char by char
  4701.   // The 'mode' is one of the 'In' states. As we go, we're adding Arguments
  4702.   // to the 'args' array.
  4703.  
  4704.   while (true) {
  4705.     var c = typed[i];
  4706.     switch (mode) {
  4707.       case In.WHITESPACE:
  4708.         if (c === '\'') {
  4709.           prefix = typed.substring(start, i + 1);
  4710.           mode = In.SINGLE_Q;
  4711.           start = i + 1;
  4712.         }
  4713.         else if (c === '"') {
  4714.           prefix = typed.substring(start, i + 1);
  4715.           mode = In.DOUBLE_Q;
  4716.           start = i + 1;
  4717.         }
  4718.         else if (c === '{') {
  4719.           prefix = typed.substring(start, i + 1);
  4720.           mode = In.SCRIPT;
  4721.           blockDepth++;
  4722.           start = i + 1;
  4723.         }
  4724.         else if (/ /.test(c)) {
  4725.           // Still whitespace, do nothing
  4726.         }
  4727.         else {
  4728.           prefix = typed.substring(start, i);
  4729.           mode = In.SIMPLE;
  4730.           start = i;
  4731.         }
  4732.         break;
  4733.  
  4734.       case In.SIMPLE:
  4735.         // There is an edge case of xx'xx which we are assuming to
  4736.         // be a single parameter (and same with ")
  4737.         if (c === ' ') {
  4738.           var str = unescape2(typed.substring(start, i));
  4739.           args.push(new Argument(str, prefix, ''));
  4740.           mode = In.WHITESPACE;
  4741.           start = i;
  4742.           prefix = '';
  4743.         }
  4744.         break;
  4745.  
  4746.       case In.SINGLE_Q:
  4747.         if (c === '\'') {
  4748.           var str = unescape2(typed.substring(start, i));
  4749.           args.push(new Argument(str, prefix, c));
  4750.           mode = In.WHITESPACE;
  4751.           start = i + 1;
  4752.           prefix = '';
  4753.         }
  4754.         break;
  4755.  
  4756.       case In.DOUBLE_Q:
  4757.         if (c === '"') {
  4758.           var str = unescape2(typed.substring(start, i));
  4759.           args.push(new Argument(str, prefix, c));
  4760.           mode = In.WHITESPACE;
  4761.           start = i + 1;
  4762.           prefix = '';
  4763.         }
  4764.         break;
  4765.  
  4766.       case In.SCRIPT:
  4767.         if (c === '{') {
  4768.           blockDepth++;
  4769.         }
  4770.         else if (c === '}') {
  4771.           blockDepth--;
  4772.           if (blockDepth === 0) {
  4773.             var str = unescape2(typed.substring(start, i));
  4774.             args.push(new ScriptArgument(str, prefix, c));
  4775.             mode = In.WHITESPACE;
  4776.             start = i + 1;
  4777.             prefix = '';
  4778.           }
  4779.         }
  4780.         break;
  4781.     }
  4782.  
  4783.     i++;
  4784.  
  4785.     if (i >= typed.length) {
  4786.       // There is nothing else to read - tidy up
  4787.       if (mode === In.WHITESPACE) {
  4788.         if (i !== start) {
  4789.           // There's whitespace at the end of the typed string. Add it to the
  4790.           // last argument's suffix, creating an empty argument if needed.
  4791.           var extra = typed.substring(start, i);
  4792.           var lastArg = args[args.length - 1];
  4793.           if (!lastArg) {
  4794.             args.push(new Argument('', extra, ''));
  4795.           }
  4796.           else {
  4797.             lastArg.suffix += extra;
  4798.           }
  4799.         }
  4800.       }
  4801.       else if (mode === In.SCRIPT) {
  4802.         var str = unescape2(typed.substring(start, i + 1));
  4803.         args.push(new ScriptArgument(str, prefix, ''));
  4804.       }
  4805.       else {
  4806.         var str = unescape2(typed.substring(start, i + 1));
  4807.         args.push(new Argument(str, prefix, ''));
  4808.       }
  4809.       break;
  4810.     }
  4811.   }
  4812.  
  4813.   return args;
  4814. };
  4815.  
  4816. /**
  4817.  * If the input has no spaces, quotes, braces or escapes,
  4818.  * we can take the fast track.
  4819.  */
  4820. function isSimple(typed) {
  4821.    for (var i = 0; i < typed.length; i++) {
  4822.      var c = typed.charAt(i);
  4823.      if (c === ' ' || c === '"' || c === '\'' ||
  4824.          c === '{' || c === '}' || c === '\\') {
  4825.        return false;
  4826.      }
  4827.    }
  4828.    return true;
  4829. }
  4830.  
  4831. /**
  4832.  * Looks in the canon for a command extension that matches what has been
  4833.  * typed at the command line.
  4834.  */
  4835. Requisition.prototype._split = function(args) {
  4836.   // Handle the special case of the user typing { javascript(); }
  4837.   // We use the hidden 'eval' command directly rather than shift()ing one of
  4838.   // the parameters, and parse()ing it.
  4839.   if (args[0] instanceof ScriptArgument) {
  4840.     // Special case: if the user enters { console.log('foo'); } then we need to
  4841.     // use the hidden 'eval' command
  4842.     var conversion = new Conversion(evalCommand, new Argument());
  4843.     this.commandAssignment.setConversion(conversion);
  4844.     return;
  4845.   }
  4846.  
  4847.   var argsUsed = 1;
  4848.   var conversion;
  4849.  
  4850.   while (argsUsed <= args.length) {
  4851.     var arg = (argsUsed === 1) ?
  4852.       args[0] :
  4853.       new MergedArgument(args, 0, argsUsed);
  4854.     conversion = this.commandAssignment.param.type.parse(arg);
  4855.  
  4856.     // We only want to carry on if this command is a parent command,
  4857.     // which means that there is a commandAssignment, but not one with
  4858.     // an exec function.
  4859.     if (!conversion.value || conversion.value.exec) {
  4860.       break;
  4861.     }
  4862.  
  4863.     // Previously we needed a way to hide commands depending context.
  4864.     // We have not resurrected that feature yet, but if we do we should
  4865.     // insert code here to ignore certain commands depending on the
  4866.     // context/environment
  4867.  
  4868.     argsUsed++;
  4869.   }
  4870.  
  4871.   this.commandAssignment.setConversion(conversion);
  4872.  
  4873.   for (var i = 0; i < argsUsed; i++) {
  4874.     args.shift();
  4875.   }
  4876.  
  4877.   // This could probably be re-written to consume args as we go
  4878. };
  4879.  
  4880. /**
  4881.  * Work out which arguments are applicable to which parameters.
  4882.  */
  4883. Requisition.prototype._assign = function(args) {
  4884.   if (!this.commandAssignment.getValue()) {
  4885.     this._unassigned.setUnassigned(args);
  4886.     return;
  4887.   }
  4888.  
  4889.   if (args.length === 0) {
  4890.     this.setDefaultArguments();
  4891.     this._unassigned.setDefault();
  4892.     return;
  4893.   }
  4894.  
  4895.   // Create an error if the command does not take parameters, but we have
  4896.   // been given them ...
  4897.   if (this.assignmentCount === 0) {
  4898.     this._unassigned.setUnassigned(args);
  4899.     return;
  4900.   }
  4901.  
  4902.   // Special case: if there is only 1 parameter, and that's of type
  4903.   // text, then we put all the params into the first param
  4904.   if (this.assignmentCount === 1) {
  4905.     var assignment = this.getAssignment(0);
  4906.     if (assignment.param.type instanceof StringType) {
  4907.       var arg = (args.length === 1) ?
  4908.         args[0] :
  4909.         new MergedArgument(args);
  4910.       var conversion = assignment.param.type.parse(arg);
  4911.       assignment.setConversion(conversion);
  4912.       this._unassigned.setDefault();
  4913.       return;
  4914.     }
  4915.   }
  4916.  
  4917.   // Positional arguments can still be specified by name, but if they are
  4918.   // then we need to ignore them when working them out positionally
  4919.   var names = this.getParameterNames();
  4920.  
  4921.   // We collect the arguments used in arrays here before assigning
  4922.   var arrayArgs = {};
  4923.  
  4924.   // Extract all the named parameters
  4925.   this.getAssignments(false).forEach(function(assignment) {
  4926.     // Loop over the arguments
  4927.     // Using while rather than loop because we remove args as we go
  4928.     var i = 0;
  4929.     while (i < args.length) {
  4930.       if (assignment.param.isKnownAs(args[i].text)) {
  4931.         var arg = args.splice(i, 1)[0];
  4932.         names = names.filter(function(test) {
  4933.           return test !== assignment.param.name;
  4934.         });
  4935.  
  4936.         // boolean parameters don't have values, default to false
  4937.         if (assignment.param.type instanceof BooleanType) {
  4938.           arg = new TrueNamedArgument(null, arg);
  4939.         }
  4940.         else {
  4941.           var valueArg = null;
  4942.           if (i + 1 >= args.length) {
  4943.             valueArg = args.splice(i, 1)[0];
  4944.           }
  4945.           arg = new NamedArgument(arg, valueArg);
  4946.         }
  4947.  
  4948.         if (assignment.param.type instanceof ArrayType) {
  4949.           var arrayArg = arrayArgs[assignment.param.name];
  4950.           if (!arrayArg) {
  4951.             arrayArg = new ArrayArgument();
  4952.             arrayArgs[assignment.param.name] = arrayArg;
  4953.           }
  4954.           arrayArg.addArgument(arg);
  4955.         }
  4956.         else {
  4957.           var conversion = assignment.param.type.parse(arg);
  4958.           assignment.setConversion(conversion);
  4959.         }
  4960.       }
  4961.       else {
  4962.         // Skip this parameter and handle as a positional parameter
  4963.         i++;
  4964.       }
  4965.     }
  4966.   }, this);
  4967.  
  4968.   // What's left are positional parameters assign in order
  4969.   names.forEach(function(name) {
  4970.     var assignment = this.getAssignment(name);
  4971.  
  4972.     // If not set positionally, and we can't set it non-positionally,
  4973.     // we have to default it to prevent previous values surviving
  4974.     if (!assignment.param.isPositionalAllowed()) {
  4975.       assignment.setDefault();
  4976.       return;
  4977.     }
  4978.  
  4979.     // If this is a positional array argument, then it swallows the
  4980.     // rest of the arguments.
  4981.     if (assignment.param.type instanceof ArrayType) {
  4982.       var arrayArg = arrayArgs[assignment.param.name];
  4983.       if (!arrayArg) {
  4984.         arrayArg = new ArrayArgument();
  4985.         arrayArgs[assignment.param.name] = arrayArg;
  4986.       }
  4987.       arrayArg.addArguments(args);
  4988.       args = [];
  4989.     }
  4990.     else {
  4991.       var arg = (args.length > 0) ?
  4992.           args.splice(0, 1)[0] :
  4993.           new Argument();
  4994.  
  4995.       var conversion = assignment.param.type.parse(arg);
  4996.       assignment.setConversion(conversion);
  4997.     }
  4998.   }, this);
  4999.  
  5000.   // Now we need to assign the array argument (if any)
  5001.   Object.keys(arrayArgs).forEach(function(name) {
  5002.     var assignment = this.getAssignment(name);
  5003.     var conversion = assignment.param.type.parse(arrayArgs[name]);
  5004.     assignment.setConversion(conversion);
  5005.   }, this);
  5006.  
  5007.   if (args.length > 0) {
  5008.     this._unassigned.setUnassigned(args);
  5009.   }
  5010.   else {
  5011.     this._unassigned.setDefault();
  5012.   }
  5013. };
  5014.  
  5015. exports.Requisition = Requisition;
  5016.  
  5017.  
  5018. /**
  5019.  * Functions and data related to the execution of a command
  5020.  */
  5021. function ExecutionContext(environment, document) {
  5022.   this.environment = environment;
  5023.   this.document = document;
  5024. }
  5025.  
  5026. ExecutionContext.prototype.createPromise = function() {
  5027.   return new Promise();
  5028. };
  5029.  
  5030.  
  5031. });
  5032. /*
  5033.  * Copyright 2009-2011 Mozilla Foundation and contributors
  5034.  * Licensed under the New BSD license. See LICENSE.txt or:
  5035.  * http://opensource.org/licenses/BSD-3-Clause
  5036.  */
  5037.  
  5038. define('gcli/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
  5039.  
  5040.   Components.utils.import("resource:///modules/devtools/Promise.jsm");
  5041.   exports.Promise = Promise;
  5042.  
  5043. });
  5044. /*
  5045.  * Copyright 2009-2011 Mozilla Foundation and contributors
  5046.  * Licensed under the New BSD license. See LICENSE.txt or:
  5047.  * http://opensource.org/licenses/BSD-3-Clause
  5048.  */
  5049.  
  5050. define('gcli/ui/inputter', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types', 'gcli/history', 'text!gcli/ui/inputter.css'], function(require, exports, module) {
  5051. var cliView = exports;
  5052.  
  5053.  
  5054. var KeyEvent = require('gcli/util').event.KeyEvent;
  5055. var dom = require('gcli/util').dom;
  5056.  
  5057. var Status = require('gcli/types').Status;
  5058. var History = require('gcli/history').History;
  5059.  
  5060. var inputterCss = require('text!gcli/ui/inputter.css');
  5061.  
  5062.  
  5063. /**
  5064.  * A wrapper to take care of the functions concerning an input element
  5065.  */
  5066. function Inputter(options) {
  5067.   this.requisition = options.requisition;
  5068.  
  5069.   // Suss out where the input element is
  5070.   this.element = options.inputElement || 'gcliInput';
  5071.   if (typeof this.element === 'string') {
  5072.     this.document = options.document || document;
  5073.     var name = this.element;
  5074.     this.element = this.document.getElementById(name);
  5075.     if (!this.element) {
  5076.       throw new Error('No element with id=' + name + '.');
  5077.     }
  5078.   }
  5079.   else {
  5080.     // Assume we've been passed in the correct node
  5081.     this.document = this.element.ownerDocument;
  5082.   }
  5083.  
  5084.   if (inputterCss != null) {
  5085.     this.style = dom.importCss(inputterCss, this.document);
  5086.   }
  5087.  
  5088.   this.element.spellcheck = false;
  5089.  
  5090.   // Used to distinguish focus from TAB in CLI. See onKeyUp()
  5091.   this.lastTabDownAt = 0;
  5092.  
  5093.   // Used to effect caret changes. See _processCaretChange()
  5094.   this._caretChange = null;
  5095.  
  5096.   // Ensure that TAB/UP/DOWN isn't handled by the browser
  5097.   this.onKeyDown = this.onKeyDown.bind(this);
  5098.   this.onKeyUp = this.onKeyUp.bind(this);
  5099.   this.element.addEventListener('keydown', this.onKeyDown, false);
  5100.   this.element.addEventListener('keyup', this.onKeyUp, false);
  5101.  
  5102.   if (options.completer == null) {
  5103.     options.completer = new Completer(options);
  5104.   }
  5105.   else if (typeof options.completer === 'function') {
  5106.     options.completer = new options.completer(options);
  5107.   }
  5108.   this.completer = options.completer;
  5109.   this.completer.decorate(this);
  5110.  
  5111.   // Use the provided history object, or instantiate our own
  5112.   this.history = options.history = options.history || new History(options);
  5113.   this._scrollingThroughHistory = false;
  5114.  
  5115.   // Cursor position affects hint severity
  5116.   this.onMouseUp = function(ev) {
  5117.     this.completer.update(this.getInputState());
  5118.   }.bind(this);
  5119.   this.element.addEventListener('mouseup', this.onMouseUp, false);
  5120.  
  5121.   this.focusManager = options.focusManager;
  5122.   if (this.focusManager) {
  5123.     this.focusManager.addMonitoredElement(this.element, 'input');
  5124.   }
  5125.  
  5126.   this.requisition.inputChange.add(this.onInputChange, this);
  5127. }
  5128.  
  5129. /**
  5130.  * Avoid memory leaks
  5131.  */
  5132. Inputter.prototype.destroy = function() {
  5133.   this.requisition.inputChange.remove(this.onInputChange, this);
  5134.   if (this.focusManager) {
  5135.     this.focusManager.removeMonitoredElement(this.element, 'input');
  5136.   }
  5137.  
  5138.   this.element.removeEventListener('keydown', this.onKeyDown, false);
  5139.   this.element.removeEventListener('keyup', this.onKeyUp, false);
  5140.   delete this.onKeyDown;
  5141.   delete this.onKeyUp;
  5142.  
  5143.   this.history.destroy();
  5144.   this.completer.destroy();
  5145.  
  5146.   if (this.style) {
  5147.     this.style.parentNode.removeChild(this.style);
  5148.     delete this.style;
  5149.   }
  5150.  
  5151.   delete this.document;
  5152.   delete this.element;
  5153. };
  5154.  
  5155. /**
  5156.  * Utility to add an element into the DOM after the input element
  5157.  */
  5158. Inputter.prototype.appendAfter = function(element) {
  5159.   this.element.parentNode.insertBefore(element, this.element.nextSibling);
  5160. };
  5161.  
  5162. /**
  5163.  * Handler for the Requisition.inputChange event
  5164.  */
  5165. Inputter.prototype.onInputChange = function() {
  5166.   if (this._caretChange == null) {
  5167.     // We weren't expecting a change so this was requested by the hint system
  5168.     // we should move the cursor to the end of the 'changed section', and the
  5169.     // best we can do for that right now is the end of the current argument.
  5170.     this._caretChange = Caret.TO_ARG_END;
  5171.   }
  5172.   this._setInputInternal(this.requisition.toString());
  5173. };
  5174.  
  5175. /**
  5176.  * Internal function to set the input field to a value.
  5177.  * This function checks to see if the current value is the same as the new
  5178.  * value, and makes no changes if they are the same (except for caret/completer
  5179.  * updating - see below). If changes are to be made, they are done in a timeout
  5180.  * to avoid XUL bug 676520.
  5181.  * This function assumes that the data model is up to date with the new value.
  5182.  * It does attempts to leave the caret position in the same position in the
  5183.  * input string unless this._caretChange === Caret.TO_ARG_END. This is required
  5184.  * for completion events.
  5185.  * It does not change the completer decoration unless this._updatePending is
  5186.  * set. This is required for completion events.
  5187.  */
  5188. Inputter.prototype._setInputInternal = function(str, update) {
  5189.   if (!this.document) {
  5190.     return; // This can happen post-destroy()
  5191.   }
  5192.  
  5193.   if (this.element.value && this.element.value === str) {
  5194.     this._processCaretChange(this.getInputState(), false);
  5195.     return;
  5196.   }
  5197.  
  5198.   // Updating in a timeout fixes a XUL issue (bug 676520) where textbox gives
  5199.   // incorrect values for its content
  5200.   this.document.defaultView.setTimeout(function() {
  5201.     if (!this.document) {
  5202.       return; // This can happen post-destroy()
  5203.     }
  5204.  
  5205.     // Bug 678520 - We could do better caret handling by recording the caret
  5206.     // position in terms of offset into an assignment, and then replacing into
  5207.     // a similar place
  5208.     var input = this.getInputState();
  5209.     input.typed = str;
  5210.     this._processCaretChange(input);
  5211.     this.element.value = str;
  5212.  
  5213.     if (update) {
  5214.       this.update();
  5215.     }
  5216.   }.bind(this), 0);
  5217. };
  5218.  
  5219. /**
  5220.  * Various ways in which we need to manipulate the caret/selection position.
  5221.  * A value of null means we're not expecting a change
  5222.  */
  5223. var Caret = {
  5224.   /**
  5225.    * We are expecting changes, but we don't need to move the cursor
  5226.    */
  5227.   NO_CHANGE: 0,
  5228.  
  5229.   /**
  5230.    * We want the entire input area to be selected
  5231.    */
  5232.   SELECT_ALL: 1,
  5233.  
  5234.   /**
  5235.    * The whole input has changed - push the cursor to the end
  5236.    */
  5237.   TO_END: 2,
  5238.  
  5239.   /**
  5240.    * A part of the input has changed - push the cursor to the end of the
  5241.    * changed section
  5242.    */
  5243.   TO_ARG_END: 3
  5244. };
  5245.  
  5246. /**
  5247.  * If this._caretChange === Caret.TO_ARG_END, we alter the input object to move
  5248.  * the selection start to the end of the current argument.
  5249.  * @param input An object shaped like { typed:'', cursor: { start:0, end:0 }}
  5250.  * @param forceUpdate Do we call this.completer.update even when the cursor has
  5251.  * not changed (useful when input.typed has changed)
  5252.  */
  5253. Inputter.prototype._processCaretChange = function(input, forceUpdate) {
  5254.   var start, end;
  5255.   switch (this._caretChange) {
  5256.     case Caret.SELECT_ALL:
  5257.       start = 0;
  5258.       end = input.typed.length;
  5259.       break;
  5260.  
  5261.     case Caret.TO_END:
  5262.       start = input.typed.length;
  5263.       end = input.typed.length;
  5264.       break;
  5265.  
  5266.     case Caret.TO_ARG_END:
  5267.       // There could be a fancy way to do this involving assignment/arg math
  5268.       // but it doesn't seem easy, so we cheat a move the cursor to just before
  5269.       // the next space, or the end of the input
  5270.       start = input.cursor.start;
  5271.       do {
  5272.         start++;
  5273.       }
  5274.       while (start < input.typed.length && input.typed[start - 1] !== ' ');
  5275.  
  5276.       end = start;
  5277.       break;
  5278.  
  5279.     case null:
  5280.     case Caret.NO_CHANGE:
  5281.       start = input.cursor.start;
  5282.       end = input.cursor.end;
  5283.       break;
  5284.   }
  5285.  
  5286.   start = (start > input.typed.length) ? input.typed.length : start;
  5287.   end = (end > input.typed.length) ? input.typed.length : end;
  5288.  
  5289.   var newInput = { typed: input.typed, cursor: { start: start, end: end }};
  5290.   if (start !== input.cursor.start || end !== input.cursor.end || forceUpdate) {
  5291.     this.completer.update(newInput);
  5292.   }
  5293.  
  5294.   this.element.selectionStart = newInput.cursor.start;
  5295.   this.element.selectionEnd = newInput.cursor.end;
  5296.  
  5297.   this._caretChange = null;
  5298.   return newInput;
  5299. };
  5300.  
  5301. /**
  5302.  * Set the input field to a value.
  5303.  * This function updates the data model and the completer decoration. It sets
  5304.  * the caret to the end of the input. It does not make any similarity checks
  5305.  * so calling this function with it's current value resets the cursor position.
  5306.  * It does not execute the input or affect the history.
  5307.  * This function should not be called internally, by Inputter and never as a
  5308.  * result of a keyboard event on this.element or bug 676520 could be triggered.
  5309.  */
  5310. Inputter.prototype.setInput = function(str) {
  5311.   this.element.value = str;
  5312.   this.update();
  5313. };
  5314.  
  5315. /**
  5316.  * Focus the input element
  5317.  */
  5318. Inputter.prototype.focus = function() {
  5319.   this.element.focus();
  5320. };
  5321.  
  5322. /**
  5323.  * Ensure certain keys (arrows, tab, etc) that we would like to handle
  5324.  * are not handled by the browser
  5325.  */
  5326. Inputter.prototype.onKeyDown = function(ev) {
  5327.   if (ev.keyCode === KeyEvent.DOM_VK_UP || ev.keyCode === KeyEvent.DOM_VK_DOWN) {
  5328.     ev.preventDefault();
  5329.   }
  5330.   if (ev.keyCode === KeyEvent.DOM_VK_TAB) {
  5331.     this.lastTabDownAt = 0;
  5332.     if (!ev.shiftKey) {
  5333.       ev.preventDefault();
  5334.       // Record the timestamp of this TAB down so onKeyUp can distinguish
  5335.       // focus from TAB in the CLI.
  5336.       this.lastTabDownAt = ev.timeStamp;
  5337.     }
  5338.     if (ev.metaKey || ev.altKey || ev.crtlKey) {
  5339.       if (this.document.commandDispatcher) {
  5340.         this.document.commandDispatcher.advanceFocus();
  5341.       }
  5342.       else {
  5343.         this.element.blur();
  5344.       }
  5345.     }
  5346.   }
  5347. };
  5348.  
  5349. /**
  5350.  * The main keyboard processing loop
  5351.  */
  5352. Inputter.prototype.onKeyUp = function(ev) {
  5353.   // RETURN does a special exec/highlight thing
  5354.   if (ev.keyCode === KeyEvent.DOM_VK_RETURN) {
  5355.     var worst = this.requisition.getStatus();
  5356.     // Deny RETURN unless the command might work
  5357.     if (worst === Status.VALID) {
  5358.       this._scrollingThroughHistory = false;
  5359.       this.history.add(this.element.value);
  5360.       this.requisition.exec();
  5361.     }
  5362.     // See bug 664135 - On pressing return with an invalid input, GCLI
  5363.     // should select the incorrect part of the input for an easy fix
  5364.     return;
  5365.   }
  5366.  
  5367.   if (ev.keyCode === KeyEvent.DOM_VK_TAB && !ev.shiftKey) {
  5368.     // If the TAB keypress took the cursor from another field to this one,
  5369.     // then they get the keydown/keypress, and we get the keyup. In this
  5370.     // case we don't want to do any completion.
  5371.     // If the time of the keydown/keypress of TAB was close (i.e. within
  5372.     // 1 second) to the time of the keyup then we assume that we got them
  5373.     // both, and do the completion.
  5374.     if (this.lastTabDownAt + 1000 > ev.timeStamp) {
  5375.       this.getCurrentAssignment().complete();
  5376.       // It's possible for TAB to not change the input, in which case the
  5377.       // onInputChange event will not fire, and the caret move will not be
  5378.       // processed. So we check that this is done
  5379.       this._caretChange = Caret.TO_ARG_END;
  5380.       this._processCaretChange(this.getInputState(), true);
  5381.     }
  5382.     this.lastTabDownAt = 0;
  5383.     this._scrollingThroughHistory = false;
  5384.     return;
  5385.   }
  5386.  
  5387.   if (ev.keyCode === KeyEvent.DOM_VK_UP) {
  5388.     if (this.element.value === '' || this._scrollingThroughHistory) {
  5389.       this._scrollingThroughHistory = true;
  5390.       this._setInputInternal(this.history.backward(), true);
  5391.     }
  5392.     else {
  5393.       this.getCurrentAssignment().increment();
  5394.     }
  5395.     return;
  5396.   }
  5397.  
  5398.   if (ev.keyCode === KeyEvent.DOM_VK_DOWN) {
  5399.     if (this.element.value === '' || this._scrollingThroughHistory) {
  5400.       this._scrollingThroughHistory = true;
  5401.       this._setInputInternal(this.history.forward(), true);
  5402.     }
  5403.     else {
  5404.       this.getCurrentAssignment().decrement();
  5405.     }
  5406.     return;
  5407.   }
  5408.  
  5409.   this._scrollingThroughHistory = false;
  5410.   this._caretChange = Caret.NO_CHANGE;
  5411.   this.update();
  5412. };
  5413.  
  5414. /**
  5415.  * Accessor for the assignment at the cursor.
  5416.  * i.e Requisition.getAssignmentAt(cursorPos);
  5417.  */
  5418. Inputter.prototype.getCurrentAssignment = function() {
  5419.   var start = this.element.selectionStart;
  5420.   return this.requisition.getAssignmentAt(start);
  5421. };
  5422.  
  5423. /**
  5424.  * Actually parse the input and make sure we're all up to date
  5425.  */
  5426. Inputter.prototype.update = function() {
  5427.   var input = this.getInputState();
  5428.   this.requisition.update(input);
  5429.   this.completer.update(input);
  5430. };
  5431.  
  5432. /**
  5433.  * Pull together an input object, which may include XUL hacks
  5434.  */
  5435. Inputter.prototype.getInputState = function() {
  5436.   var input = {
  5437.     typed: this.element.value,
  5438.     cursor: {
  5439.       start: this.element.selectionStart,
  5440.       end: this.element.selectionEnd
  5441.     }
  5442.   };
  5443.  
  5444.   // Workaround for potential XUL bug 676520 where textbox gives incorrect
  5445.   // values for its content
  5446.   if (input.typed == null) {
  5447.     input = { typed: '', cursor: { start: 0, end: 0 } };
  5448.     console.log('fixing input.typed=""', input);
  5449.   }
  5450.  
  5451.   return input;
  5452. };
  5453.  
  5454. cliView.Inputter = Inputter;
  5455.  
  5456.  
  5457. /**
  5458.  * Completer is an 'input-like' element that sits  an input element annotating
  5459.  * it with visual goodness.
  5460.  * @param {object} options An object that contains various options which
  5461.  * customizes how the completer functions.
  5462.  * Properties on the options object:
  5463.  * - document (required) DOM document to be used in creating elements
  5464.  * - requisition (required) A GCLI Requisition object whose state is monitored
  5465.  * - completeElement (optional) An element to use
  5466.  * - completionPrompt (optional) The prompt to show before a completion.
  5467.  *   Defaults to '»' (double greater-than, a.k.a right guillemet).
  5468.  */
  5469. function Completer(options) {
  5470.   this.document = options.document;
  5471.   this.requisition = options.requisition;
  5472.   this.elementCreated = false;
  5473.  
  5474.   this.element = options.completeElement || 'gcliComplete';
  5475.   if (typeof this.element === 'string') {
  5476.     var name = this.element;
  5477.     this.element = this.document.getElementById(name);
  5478.  
  5479.     if (!this.element) {
  5480.       this.elementCreated = true;
  5481.       this.element = dom.createElement(this.document, 'div');
  5482.       this.element.className = 'gcliCompletion gcliVALID';
  5483.       this.element.setAttribute('tabindex', '-1');
  5484.       this.element.setAttribute('aria-live', 'polite');
  5485.     }
  5486.   }
  5487.  
  5488.   this.completionPrompt = typeof options.completionPrompt === 'string'
  5489.     ? options.completionPrompt
  5490.     : '»';
  5491.  
  5492.   if (options.inputBackgroundElement) {
  5493.     this.backgroundElement = options.inputBackgroundElement;
  5494.   }
  5495.   else {
  5496.     this.backgroundElement = this.element;
  5497.   }
  5498. }
  5499.  
  5500. /**
  5501.  * Avoid memory leaks
  5502.  */
  5503. Completer.prototype.destroy = function() {
  5504.   delete this.document;
  5505.   delete this.element;
  5506.   delete this.backgroundElement;
  5507.  
  5508.   if (this.elementCreated) {
  5509.     this.document.defaultView.removeEventListener('resize', this.resizer, false);
  5510.   }
  5511.  
  5512.   delete this.inputter;
  5513. };
  5514.  
  5515. /**
  5516.  * A list of the styles that decorate() should copy to make the completion
  5517.  * element look like the input element. backgroundColor is a spiritual part of
  5518.  * this list, but see comment in decorate().
  5519.  */
  5520. Completer.copyStyles = [ 'fontSize', 'fontFamily', 'fontWeight', 'fontStyle' ];
  5521.  
  5522. /**
  5523.  * Make ourselves visually similar to the input element, and make the input
  5524.  * element transparent so our background shines through
  5525.  */
  5526. Completer.prototype.decorate = function(inputter) {
  5527.   this.inputter = inputter;
  5528.   var input = inputter.element;
  5529.  
  5530.   // If we were told which element to use, then assume it is already
  5531.   // correctly positioned. Otherwise insert it alongside the input element
  5532.   if (this.elementCreated) {
  5533.     this.inputter.appendAfter(this.element);
  5534.  
  5535.     var styles = this.document.defaultView.getComputedStyle(input, null);
  5536.     Completer.copyStyles.forEach(function(style) {
  5537.       this.element.style[style] = styles[style];
  5538.     }, this);
  5539.  
  5540.     // The completer text is by default invisible so we make it the same color
  5541.     // as the input background.
  5542.     this.element.style.color = input.style.backgroundColor;
  5543.  
  5544.     // If there is a separate backgroundElement, then we make the element
  5545.     // transparent, otherwise it inherits the color of the input node
  5546.     // It's not clear why backgroundColor doesn't work when used from
  5547.     // computedStyle, but it doesn't. Patches welcome!
  5548.     this.element.style.backgroundColor = (this.backgroundElement != this.element) ?
  5549.         'transparent' :
  5550.         input.style.backgroundColor;
  5551.     input.style.backgroundColor = 'transparent';
  5552.  
  5553.     // Make room for the prompt
  5554.     input.style.paddingLeft = '20px';
  5555.  
  5556.     this.resizer = this.resizer.bind(this);
  5557.     this.document.defaultView.addEventListener('resize', this.resizer, false);
  5558.     this.resizer();
  5559.   }
  5560. };
  5561.  
  5562. /**
  5563.  * Ensure that the completion element is the same size and the inputter element
  5564.  */
  5565. Completer.prototype.resizer = function() {
  5566.   var rect = this.inputter.element.getBoundingClientRect();
  5567.   // -4 to line up with 1px of padding and border, top and bottom
  5568.   var height = rect.bottom - rect.top - 4;
  5569.  
  5570.   this.element.style.top = rect.top + 'px';
  5571.   this.element.style.height = height + 'px';
  5572.   this.element.style.lineHeight = height + 'px';
  5573.   this.element.style.left = rect.left + 'px';
  5574.   this.element.style.width = (rect.right - rect.left) + 'px';
  5575. };
  5576.  
  5577. /**
  5578.  * Is the completion given, a "strict" completion of the user inputted value?
  5579.  * A completion is considered "strict" only if it the user inputted value is an
  5580.  * exact prefix of the completion (ignoring leading whitespace)
  5581.  */
  5582. function isStrictCompletion(inputValue, completion) {
  5583.   // Strip any leading whitespace from the user inputted value because the
  5584.   // completion will never have leading whitespace.
  5585.   inputValue = inputValue.replace(/^\s*/, '');
  5586.   // Strict: "ec" -> "echo"
  5587.   // Non-Strict: "ls *" -> "ls foo bar baz"
  5588.   return completion.indexOf(inputValue) === 0;
  5589. }
  5590.  
  5591. /**
  5592.  * Bring the completion element up to date with what the requisition says
  5593.  */
  5594. Completer.prototype.update = function(input) {
  5595.   var current = this.requisition.getAssignmentAt(input.cursor.start);
  5596.   var predictions = current.getPredictions();
  5597.  
  5598.   var completion = '<span class="gcliPrompt">' + this.completionPrompt + '</span> ';
  5599.   if (input.typed.length > 0) {
  5600.     var scores = this.requisition.getInputStatusMarkup();
  5601.     completion += this.markupStatusScore(scores, input);
  5602.   }
  5603.  
  5604.   if (input.typed.length > 0 && predictions.length > 0) {
  5605.     var tab = predictions[0].name;
  5606.     var existing = current.getArg().text;
  5607.     if (isStrictCompletion(existing, tab) && input.cursor.start === input.typed.length) {
  5608.       // Display the suffix of the prediction as the completion.
  5609.       var numLeadingSpaces = existing.match(/^(\s*)/)[0].length;
  5610.       var suffix = tab.slice(existing.length - numLeadingSpaces);
  5611.       completion += '<span class="gcliCompl">' + suffix + '</span>';
  5612.     } else {
  5613.       // Display the '-> prediction' at the end of the completer element
  5614.       completion += '  <span class="gcliCompl">⇥ ' +
  5615.           tab + '</span>';
  5616.     }
  5617.   }
  5618.  
  5619.   // A hack to add a grey '}' to the end of the command line when we've opened
  5620.   // with a { but haven't closed it
  5621.   var command = this.requisition.commandAssignment.getValue();
  5622.   if (command && command.name === '{') {
  5623.     if (this.requisition.getAssignment(0).getArg().suffix.indexOf('}') === -1) {
  5624.       completion += '<span class="gcliCloseBrace">}</span>';
  5625.     }
  5626.   }
  5627.  
  5628.   dom.setInnerHtml(this.element, '<span>' + completion + '</span>');
  5629. };
  5630.  
  5631. /**
  5632.  * Mark-up an array of Status values with spans
  5633.  */
  5634. Completer.prototype.markupStatusScore = function(scores, input) {
  5635.   var completion = '';
  5636.   if (scores.length === 0) {
  5637.     return completion;
  5638.   }
  5639.  
  5640.   var i = 0;
  5641.   var lastStatus = -1;
  5642.   while (true) {
  5643.     if (lastStatus !== scores[i]) {
  5644.       var state = scores[i];
  5645.       if (!state) {
  5646.         console.error('No state at i=' + i + '. scores.len=' + scores.length);
  5647.         state = Status.VALID;
  5648.       }
  5649.       completion += '<span class="gcli' + state.toString() + '">';
  5650.       lastStatus = scores[i];
  5651.     }
  5652.     var char = input.typed[i];
  5653.     if (char === ' ') {
  5654.       char = ' ';
  5655.     }
  5656.     completion += char;
  5657.     i++;
  5658.     if (i === input.typed.length) {
  5659.       completion += '</span>';
  5660.       break;
  5661.     }
  5662.     if (lastStatus !== scores[i]) {
  5663.       completion += '</span>';
  5664.     }
  5665.   }
  5666.  
  5667.   return completion;
  5668. };
  5669.  
  5670. cliView.Completer = Completer;
  5671.  
  5672.  
  5673. });
  5674. /*
  5675.  * Copyright 2009-2011 Mozilla Foundation and contributors
  5676.  * Licensed under the New BSD license. See LICENSE.txt or:
  5677.  * http://opensource.org/licenses/BSD-3-Clause
  5678.  */
  5679.  
  5680. define('gcli/history', ['require', 'exports', 'module' ], function(require, exports, module) {
  5681.  
  5682. /**
  5683.  * A History object remembers commands that have been entered in the past and
  5684.  * provides an API for accessing them again.
  5685.  * See Bug 681340: Search through history (like C-r in bash)?
  5686.  */
  5687. function History() {
  5688.   // This is the actual buffer where previous commands are kept.
  5689.   // 'this._buffer[0]' should always be equal the empty string. This is so
  5690.   // that when you try to go in to the "future", you will just get an empty
  5691.   // command.
  5692.   this._buffer = [''];
  5693.  
  5694.   // This is an index in to the history buffer which points to where we
  5695.   // currently are in the history.
  5696.   this._current = 0;
  5697. }
  5698.  
  5699. /**
  5700.  * Avoid memory leaks
  5701.  */
  5702. History.prototype.destroy = function() {
  5703. //  delete this._buffer;
  5704. };
  5705.  
  5706. /**
  5707.  * Record and save a new command in the history.
  5708.  */
  5709. History.prototype.add = function(command) {
  5710.   this._buffer.splice(1, 0, command);
  5711.   this._current = 0;
  5712. };
  5713.  
  5714. /**
  5715.  * Get the next (newer) command from history.
  5716.  */
  5717. History.prototype.forward = function() {
  5718.   if (this._current > 0 ) {
  5719.     this._current--;
  5720.   }
  5721.   return this._buffer[this._current];
  5722. };
  5723.  
  5724. /**
  5725.  * Get the previous (older) item from history.
  5726.  */
  5727. History.prototype.backward = function() {
  5728.   if (this._current < this._buffer.length - 1) {
  5729.     this._current++;
  5730.   }
  5731.   return this._buffer[this._current];
  5732. };
  5733.  
  5734. exports.History = History;
  5735.  
  5736. });define("text!gcli/ui/inputter.css", [], void 0);
  5737. /*
  5738.  * Copyright 2009-2011 Mozilla Foundation and contributors
  5739.  * Licensed under the New BSD license. See LICENSE.txt or:
  5740.  * http://opensource.org/licenses/BSD-3-Clause
  5741.  */
  5742.  
  5743. define('gcli/ui/arg_fetch', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types', 'gcli/ui/field', 'gcli/ui/domtemplate', 'text!gcli/ui/arg_fetch.css', 'text!gcli/ui/arg_fetch.html'], function(require, exports, module) {
  5744. var argFetch = exports;
  5745.  
  5746.  
  5747. var dom = require('gcli/util').dom;
  5748. var Status = require('gcli/types').Status;
  5749.  
  5750. var getField = require('gcli/ui/field').getField;
  5751. var Templater = require('gcli/ui/domtemplate').Templater;
  5752.  
  5753. var editorCss = require('text!gcli/ui/arg_fetch.css');
  5754. var argFetchHtml = require('text!gcli/ui/arg_fetch.html');
  5755.  
  5756.  
  5757. /**
  5758.  * A widget to display an inline dialog which allows the user to fill out
  5759.  * the arguments to a command.
  5760.  * @param document The document to use in creating widgets
  5761.  * @param requisition The Requisition to fill out
  5762.  */
  5763. function ArgFetcher(document, requisition) {
  5764.   this.document = document;
  5765.   this.requisition = requisition;
  5766.  
  5767.   // FF can be really hard to debug if doc is null, so we check early on
  5768.   if (!this.document) {
  5769.     throw new Error('No document');
  5770.   }
  5771.  
  5772.   this.element =  dom.createElement(this.document, 'div');
  5773.   this.element.className = 'gcliCliEle';
  5774.   // We cache the fields we create so we can destroy them later
  5775.   this.fields = [];
  5776.  
  5777.   this.tmpl = new Templater();
  5778.   // Populated by template
  5779.   this.okElement = null;
  5780.  
  5781.   // Pull the HTML into the DOM, but don't add it to the document
  5782.   if (editorCss != null) {
  5783.     this.style = dom.importCss(editorCss, this.document);
  5784.   }
  5785.  
  5786.   var templates = dom.createElement(this.document, 'div');
  5787.   dom.setInnerHtml(templates, argFetchHtml);
  5788.   this.reqTempl = templates.querySelector('#gcliReqTempl');
  5789.  
  5790.   this.requisition.commandChange.add(this.onCommandChange, this);
  5791.   this.requisition.inputChange.add(this.onInputChange, this);
  5792. }
  5793.  
  5794. /**
  5795.  * Avoid memory leaks
  5796.  */
  5797. ArgFetcher.prototype.destroy = function() {
  5798.   this.requisition.inputChange.remove(this.onInputChange, this);
  5799.   this.requisition.commandChange.remove(this.onCommandChange, this);
  5800.  
  5801.   if (this.style) {
  5802.     this.style.parentNode.removeChild(this.style);
  5803.     delete this.style;
  5804.   }
  5805.  
  5806.   this.fields.forEach(function(field) { field.destroy(); });
  5807.  
  5808.   delete this.document;
  5809.   delete this.element;
  5810.   delete this.okElement;
  5811.   delete this.reqTempl;
  5812. };
  5813.  
  5814. /**
  5815.  * Called whenever the command part of the requisition changes
  5816.  */
  5817. ArgFetcher.prototype.onCommandChange = function(ev) {
  5818.   var command = this.requisition.commandAssignment.getValue();
  5819.   if (!command || !command.exec) {
  5820.     this.element.style.display = 'none';
  5821.   }
  5822.   else {
  5823.     if (ev && ev.oldValue === ev.newValue) {
  5824.       // Just the text has changed
  5825.       return;
  5826.     }
  5827.  
  5828.     this.fields.forEach(function(field) { field.destroy(); });
  5829.     this.fields = [];
  5830.  
  5831.     var reqEle = this.reqTempl.cloneNode(true);
  5832.     this.tmpl.processNode(reqEle, this);
  5833.     dom.clearElement(this.element);
  5834.     this.element.appendChild(reqEle);
  5835.  
  5836.     var status = this.requisition.getStatus();
  5837.     this.okElement.disabled = (status === Status.VALID);
  5838.  
  5839.     this.element.style.display = 'block';
  5840.   }
  5841. };
  5842.  
  5843. /**
  5844.  * Called whenever the text input of the requisition changes
  5845.  */
  5846. ArgFetcher.prototype.onInputChange = function(ev) {
  5847.   var command = this.requisition.commandAssignment.getValue();
  5848.   if (command && command.exec) {
  5849.     var status = this.requisition.getStatus();
  5850.     this.okElement.disabled = (status !== Status.VALID);
  5851.   }
  5852. };
  5853.  
  5854. /**
  5855.  * Called by the template process in #onCommandChange() to get an instance
  5856.  * of field for each assignment.
  5857.  */
  5858. ArgFetcher.prototype.getInputFor = function(assignment) {
  5859.   var newField = getField(assignment.param.type, {
  5860.     document: this.document,
  5861.     type: assignment.param.type,
  5862.     name: assignment.param.name,
  5863.     requisition: this.requisition,
  5864.     required: assignment.param.isDataRequired(),
  5865.     named: !assignment.param.isPositionalAllowed()
  5866.   });
  5867.  
  5868.   // BUG 664198 - remove on delete
  5869.   newField.fieldChanged.add(function(ev) {
  5870.     assignment.setConversion(ev.conversion);
  5871.   }, this);
  5872.   assignment.assignmentChange.add(function(ev) {
  5873.     newField.setConversion(ev.conversion);
  5874.   }.bind(this));
  5875.  
  5876.   this.fields.push(newField);
  5877.   newField.setConversion(this.assignment.conversion);
  5878.  
  5879.   // Bug 681894: we add the field as a property of the assignment so that
  5880.   // #linkMessageElement() can call 'field.setMessageElement(element)'
  5881.   assignment.field = newField;
  5882.  
  5883.   return newField.element;
  5884. };
  5885.  
  5886. /**
  5887.  * Called by the template to setup an mutable message field
  5888.  */
  5889. ArgFetcher.prototype.linkMessageElement = function(assignment, element) {
  5890.   // Bug 681894: See comment in getInputFor()
  5891.   var field = assignment.field;
  5892.   delete assignment.field;
  5893.   if (field == null) {
  5894.     console.error('Missing field for ' + assignment.param.name);
  5895.     return 'Missing field';
  5896.   }
  5897.   field.setMessageElement(element);
  5898.   return '';
  5899. };
  5900.  
  5901. /**
  5902.  * Event handler added by the template menu.html
  5903.  */
  5904. ArgFetcher.prototype.onFormOk = function(ev) {
  5905.   this.requisition.exec();
  5906. };
  5907.  
  5908. /**
  5909.  * Event handler added by the template menu.html
  5910.  */
  5911. ArgFetcher.prototype.onFormCancel = function(ev) {
  5912.   this.requisition.clear();
  5913. };
  5914.  
  5915. argFetch.ArgFetcher = ArgFetcher;
  5916.  
  5917.  
  5918. });
  5919. /*
  5920.  * Copyright 2009-2011 Mozilla Foundation and contributors
  5921.  * Licensed under the New BSD license. See LICENSE.txt or:
  5922.  * http://opensource.org/licenses/BSD-3-Clause
  5923.  */
  5924.  
  5925. define('gcli/ui/field', ['require', 'exports', 'module' , 'gcli/util', 'gcli/l10n', 'gcli/argument', 'gcli/types', 'gcli/types/basic', 'gcli/types/javascript', 'gcli/ui/menu'], function(require, exports, module) {
  5926.  
  5927.  
  5928. var dom = require('gcli/util').dom;
  5929. var createEvent = require('gcli/util').createEvent;
  5930. var l10n = require('gcli/l10n');
  5931.  
  5932. var Argument = require('gcli/argument').Argument;
  5933. var TrueNamedArgument = require('gcli/argument').TrueNamedArgument;
  5934. var FalseNamedArgument = require('gcli/argument').FalseNamedArgument;
  5935. var ArrayArgument = require('gcli/argument').ArrayArgument;
  5936.  
  5937. var Status = require('gcli/types').Status;
  5938. var Conversion = require('gcli/types').Conversion;
  5939. var ArrayConversion = require('gcli/types').ArrayConversion;
  5940.  
  5941. var StringType = require('gcli/types/basic').StringType;
  5942. var NumberType = require('gcli/types/basic').NumberType;
  5943. var BooleanType = require('gcli/types/basic').BooleanType;
  5944. var BlankType = require('gcli/types/basic').BlankType;
  5945. var SelectionType = require('gcli/types/basic').SelectionType;
  5946. var DeferredType = require('gcli/types/basic').DeferredType;
  5947. var ArrayType = require('gcli/types/basic').ArrayType;
  5948. var JavascriptType = require('gcli/types/javascript').JavascriptType;
  5949.  
  5950. var Menu = require('gcli/ui/menu').Menu;
  5951.  
  5952.  
  5953. /**
  5954.  * A Field is a way to get input for a single parameter.
  5955.  * This class is designed to be inherited from. It's important that all
  5956.  * subclasses have a similar constructor signature because they are created
  5957.  * via getField(...)
  5958.  * @param document The document we use in calling createElement
  5959.  * @param type The type to use in conversions
  5960.  * @param named Is this parameter named? That is to say, are positional
  5961.  * arguments disallowed, if true, then we need to provide updates to the
  5962.  * command line that explicitly name the parameter in use (e.g. --verbose, or
  5963.  * --name Fred rather than just true or Fred)
  5964.  * @param name If this parameter is named, what name should we use
  5965.  * @param requ The requisition that we're attached to
  5966.  */
  5967. function Field(document, type, named, name, requ) {
  5968. }
  5969.  
  5970. /**
  5971.  * Subclasses should assign their element with the DOM node that gets added
  5972.  * to the 'form'. It doesn't have to be an input node, just something that
  5973.  * contains it.
  5974.  */
  5975. Field.prototype.element = undefined;
  5976.  
  5977. /**
  5978.  * Indicates that this field should drop any resources that it has created
  5979.  */
  5980. Field.prototype.destroy = function() {
  5981.   delete this.messageElement;
  5982. };
  5983.  
  5984. /**
  5985.  * Update this field display with the value from this conversion.
  5986.  * Subclasses should provide an implementation of this function.
  5987.  */
  5988. Field.prototype.setConversion = function(conversion) {
  5989.   throw new Error('Field should not be used directly');
  5990. };
  5991.  
  5992. /**
  5993.  * Extract a conversion from the values in this field.
  5994.  * Subclasses should provide an implementation of this function.
  5995.  */
  5996. Field.prototype.getConversion = function() {
  5997.   throw new Error('Field should not be used directly');
  5998. };
  5999.  
  6000. /**
  6001.  * Set the element where messages and validation errors will be displayed
  6002.  * @see setMessage()
  6003.  */
  6004. Field.prototype.setMessageElement = function(element) {
  6005.   this.messageElement = element;
  6006. };
  6007.  
  6008. /**
  6009.  * Display a validation message in the UI
  6010.  */
  6011. Field.prototype.setMessage = function(message) {
  6012.   if (this.messageElement) {
  6013.     if (message == null) {
  6014.       message = '';
  6015.     }
  6016.     dom.setInnerHtml(this.messageElement, message);
  6017.   }
  6018. };
  6019.  
  6020. /**
  6021.  * Method to be called by subclasses when their input changes, which allows us
  6022.  * to properly pass on the fieldChanged event.
  6023.  */
  6024. Field.prototype.onInputChange = function() {
  6025.   var conversion = this.getConversion();
  6026.   this.fieldChanged({ conversion: conversion });
  6027.   this.setMessage(conversion.message);
  6028. };
  6029.  
  6030. /**
  6031.  * 'static/abstract' method to allow implementations of Field to lay a claim
  6032.  * to a type. This allows claims of various strength to be weighted up.
  6033.  * See the Field.*MATCH values.
  6034.  */
  6035. Field.claim = function() {
  6036.   throw new Error('Field should not be used directly');
  6037. };
  6038. Field.MATCH = 5;
  6039. Field.DEFAULT_MATCH = 4;
  6040. Field.IF_NOTHING_BETTER = 1;
  6041. Field.NO_MATCH = 0;
  6042.  
  6043.  
  6044. /**
  6045.  * Managing the current list of Fields
  6046.  */
  6047. var fieldCtors = [];
  6048. function addField(fieldCtor) {
  6049.   if (typeof fieldCtor !== 'function') {
  6050.     console.error('addField erroring on ', fieldCtor);
  6051.     throw new Error('addField requires a Field constructor');
  6052.   }
  6053.   fieldCtors.push(fieldCtor);
  6054. }
  6055.  
  6056. function removeField(field) {
  6057.   if (typeof field !== 'string') {
  6058.     fields = fields.filter(function(test) {
  6059.       return test !== field;
  6060.     });
  6061.     delete fields[field];
  6062.   }
  6063.   else if (field instanceof Field) {
  6064.     removeField(field.name);
  6065.   }
  6066.   else {
  6067.     console.error('removeField erroring on ', field);
  6068.     throw new Error('removeField requires an instance of Field');
  6069.   }
  6070. }
  6071.  
  6072. function getField(type, options) {
  6073.   var ctor;
  6074.   var highestClaim = -1;
  6075.   fieldCtors.forEach(function(fieldCtor) {
  6076.     var claim = fieldCtor.claim(type);
  6077.     if (claim > highestClaim) {
  6078.       highestClaim = claim;
  6079.       ctor = fieldCtor;
  6080.     }
  6081.   });
  6082.  
  6083.   if (!ctor) {
  6084.     console.error('Unknown field type ', type, ' in ', fieldCtors);
  6085.     throw new Error('Can\'t find field for ' + type);
  6086.   }
  6087.  
  6088.   return new ctor(type, options);
  6089. }
  6090.  
  6091. exports.Field = Field;
  6092. exports.addField = addField;
  6093. exports.removeField = removeField;
  6094. exports.getField = getField;
  6095.  
  6096.  
  6097. /**
  6098.  * A field that allows editing of strings
  6099.  */
  6100. function StringField(type, options) {
  6101.   this.document = options.document;
  6102.   this.type = type;
  6103.   this.arg = new Argument();
  6104.  
  6105.   this.element = dom.createElement(this.document, 'input');
  6106.   this.element.type = 'text';
  6107.   this.element.style.width = '100%';
  6108.  
  6109.   this.onInputChange = this.onInputChange.bind(this);
  6110.   this.element.addEventListener('keyup', this.onInputChange, false);
  6111.  
  6112.   this.fieldChanged = createEvent('StringField.fieldChanged');
  6113. }
  6114.  
  6115. StringField.prototype = Object.create(Field.prototype);
  6116.  
  6117. StringField.prototype.destroy = function() {
  6118.   Field.prototype.destroy.call(this);
  6119.   this.element.removeEventListener('keyup', this.onInputChange, false);
  6120.   delete this.element;
  6121.   delete this.document;
  6122.   delete this.onInputChange;
  6123. };
  6124.  
  6125. StringField.prototype.setConversion = function(conversion) {
  6126.   this.arg = conversion.arg;
  6127.   this.element.value = conversion.arg.text;
  6128.   this.setMessage(conversion.message);
  6129. };
  6130.  
  6131. StringField.prototype.getConversion = function() {
  6132.   // This tweaks the prefix/suffix of the argument to fit
  6133.   this.arg = this.arg.beget(this.element.value, { prefixSpace: true });
  6134.   return this.type.parse(this.arg);
  6135. };
  6136.  
  6137. StringField.claim = function(type) {
  6138.   return type instanceof StringType ? Field.MATCH : Field.IF_NOTHING_BETTER;
  6139. };
  6140.  
  6141. exports.StringField = StringField;
  6142. addField(StringField);
  6143.  
  6144.  
  6145. /**
  6146.  * A field that allows editing of numbers using an [input type=number] field
  6147.  */
  6148. function NumberField(type, options) {
  6149.   this.document = options.document;
  6150.   this.type = type;
  6151.   this.arg = new Argument();
  6152.  
  6153.   this.element = dom.createElement(this.document, 'input');
  6154.   this.element.type = 'number';
  6155.   if (this.type.max) {
  6156.     this.element.max = this.type.max;
  6157.   }
  6158.   if (this.type.min) {
  6159.     this.element.min = this.type.min;
  6160.   }
  6161.   if (this.type.step) {
  6162.     this.element.step = this.type.step;
  6163.   }
  6164.  
  6165.   this.onInputChange = this.onInputChange.bind(this);
  6166.   this.element.addEventListener('keyup', this.onInputChange, false);
  6167.  
  6168.   this.fieldChanged = createEvent('NumberField.fieldChanged');
  6169. }
  6170.  
  6171. NumberField.prototype = Object.create(Field.prototype);
  6172.  
  6173. NumberField.claim = function(type) {
  6174.   return type instanceof NumberType ? Field.MATCH : Field.NO_MATCH;
  6175. };
  6176.  
  6177. NumberField.prototype.destroy = function() {
  6178.   Field.prototype.destroy.call(this);
  6179.   this.element.removeEventListener('keyup', this.onInputChange, false);
  6180.   delete this.element;
  6181.   delete this.document;
  6182.   delete this.onInputChange;
  6183. };
  6184.  
  6185. NumberField.prototype.setConversion = function(conversion) {
  6186.   this.arg = conversion.arg;
  6187.   this.element.value = conversion.arg.text;
  6188.   this.setMessage(conversion.message);
  6189. };
  6190.  
  6191. NumberField.prototype.getConversion = function() {
  6192.   this.arg = this.arg.beget(this.element.value, { prefixSpace: true });
  6193.   return this.type.parse(this.arg);
  6194. };
  6195.  
  6196. exports.NumberField = NumberField;
  6197. addField(NumberField);
  6198.  
  6199.  
  6200. /**
  6201.  * A field that uses a checkbox to toggle a boolean field
  6202.  */
  6203. function BooleanField(type, options) {
  6204.   this.document = options.document;
  6205.   this.type = type;
  6206.   this.name = options.name;
  6207.   this.named = options.named;
  6208.  
  6209.   this.element = dom.createElement(this.document, 'input');
  6210.   this.element.type = 'checkbox';
  6211.   this.element.id = 'gcliForm' + this.name;
  6212.  
  6213.   this.onInputChange = this.onInputChange.bind(this);
  6214.   this.element.addEventListener('change', this.onInputChange, false);
  6215.  
  6216.   this.fieldChanged = createEvent('BooleanField.fieldChanged');
  6217. }
  6218.  
  6219. BooleanField.prototype = Object.create(Field.prototype);
  6220.  
  6221. BooleanField.claim = function(type) {
  6222.   return type instanceof BooleanType ? Field.MATCH : Field.NO_MATCH;
  6223. };
  6224.  
  6225. BooleanField.prototype.destroy = function() {
  6226.   Field.prototype.destroy.call(this);
  6227.   this.element.removeEventListener('change', this.onInputChange, false);
  6228.   delete this.element;
  6229.   delete this.document;
  6230.   delete this.onInputChange;
  6231. };
  6232.  
  6233. BooleanField.prototype.setConversion = function(conversion) {
  6234.   this.element.checked = conversion.value;
  6235.   this.setMessage(conversion.message);
  6236. };
  6237.  
  6238. BooleanField.prototype.getConversion = function() {
  6239.   var value = this.element.checked;
  6240.   var arg = this.named ?
  6241.     value ? new TrueNamedArgument(this.name) : new FalseNamedArgument() :
  6242.     new Argument(' ' + value);
  6243.   return new Conversion(value, arg);
  6244. };
  6245.  
  6246. exports.BooleanField = BooleanField;
  6247. addField(BooleanField);
  6248.  
  6249.  
  6250. /**
  6251.  * Model an instanceof SelectionType as a select input box.
  6252.  * <p>There are 3 slightly overlapping concepts to be aware of:
  6253.  * <ul>
  6254.  * <li>value: This is the (probably non-string) value, known as a value by the
  6255.  *   assignment
  6256.  * <li>optValue: This is the text value as known by the DOM option element, as
  6257.  *   in <option value=???%gt...
  6258.  * <li>optText: This is the contents of the DOM option element.
  6259.  * </ul>
  6260.  */
  6261. function SelectionField(type, options) {
  6262.   this.document = options.document;
  6263.   this.type = type;
  6264.   this.items = [];
  6265.  
  6266.   this.element = dom.createElement(this.document, 'select');
  6267.   this.element.style.width = '180px';
  6268.   this._addOption({
  6269.     name: l10n.lookupFormat('fieldSelectionSelect', [ options.name ])
  6270.   });
  6271.   var lookup = this.type.getLookup();
  6272.   lookup.forEach(this._addOption, this);
  6273.  
  6274.   this.onInputChange = this.onInputChange.bind(this);
  6275.   this.element.addEventListener('change', this.onInputChange, false);
  6276.  
  6277.   this.fieldChanged = createEvent('SelectionField.fieldChanged');
  6278. }
  6279.  
  6280. SelectionField.prototype = Object.create(Field.prototype);
  6281.  
  6282. SelectionField.claim = function(type) {
  6283.   return type instanceof SelectionType ? Field.DEFAULT_MATCH : Field.NO_MATCH;
  6284. };
  6285.  
  6286. SelectionField.prototype.destroy = function() {
  6287.   Field.prototype.destroy.call(this);
  6288.   this.element.removeEventListener('change', this.onInputChange, false);
  6289.   delete this.element;
  6290.   delete this.document;
  6291.   delete this.onInputChange;
  6292. };
  6293.  
  6294. SelectionField.prototype.setConversion = function(conversion) {
  6295.   var index;
  6296.   this.items.forEach(function(item) {
  6297.     if (item.value && item.value === conversion.value) {
  6298.       index = item.index;
  6299.     }
  6300.   }, this);
  6301.   this.element.value = index;
  6302.   this.setMessage(conversion.message);
  6303. };
  6304.  
  6305. SelectionField.prototype.getConversion = function() {
  6306.   var item = this.items[this.element.value];
  6307.   var arg = new Argument(item.name, ' ');
  6308.   var value = item.value ? item.value : item;
  6309.   return new Conversion(value, arg);
  6310. };
  6311.  
  6312. SelectionField.prototype._addOption = function(item) {
  6313.   item.index = this.items.length;
  6314.   this.items.push(item);
  6315.  
  6316.   var option = dom.createElement(this.document, 'option');
  6317.   option.innerHTML = item.name;
  6318.   option.value = item.index;
  6319.   this.element.appendChild(option);
  6320. };
  6321.  
  6322. exports.SelectionField = SelectionField;
  6323. addField(SelectionField);
  6324.  
  6325.  
  6326. /**
  6327.  * A field that allows editing of javascript
  6328.  */
  6329. function JavascriptField(type, options) {
  6330.   this.document = options.document;
  6331.   this.type = type;
  6332.   this.requ = options.requisition;
  6333.  
  6334.   this.onInputChange = this.onInputChange.bind(this);
  6335.   this.arg = new Argument('', '{ ', ' }');
  6336.  
  6337.   this.element = dom.createElement(this.document, 'div');
  6338.  
  6339.   this.input = dom.createElement(this.document, 'input');
  6340.   this.input.type = 'text';
  6341.   this.input.addEventListener('keyup', this.onInputChange, false);
  6342.   this.input.style.marginBottom = '0px';
  6343.   this.input.style.width = options.name.length === 0 ? '240px' : '160px';
  6344.   this.element.appendChild(this.input);
  6345.  
  6346.   this.menu = new Menu(this.document, { field: true });
  6347.   this.element.appendChild(this.menu.element);
  6348.  
  6349.   this.setConversion(this.type.parse(new Argument('')));
  6350.  
  6351.   this.fieldChanged = createEvent('JavascriptField.fieldChanged');
  6352.  
  6353.   // i.e. Register this.onItemClick as the default action for a menu click
  6354.   this.menu.onItemClick = this.onItemClick.bind(this);
  6355. }
  6356.  
  6357. JavascriptField.prototype = Object.create(Field.prototype);
  6358.  
  6359. JavascriptField.claim = function(type) {
  6360.   return type instanceof JavascriptType ? Field.MATCH : Field.NO_MATCH;
  6361. };
  6362.  
  6363. JavascriptField.prototype.destroy = function() {
  6364.   Field.prototype.destroy.call(this);
  6365.   this.input.removeEventListener('keyup', this.onInputChange, false);
  6366.   this.menu.destroy();
  6367.   delete this.element;
  6368.   delete this.input;
  6369.   delete this.menu;
  6370.   delete this.document;
  6371.   delete this.onInputChange;
  6372. };
  6373.  
  6374. JavascriptField.prototype.setConversion = function(conversion) {
  6375.   this.arg = conversion.arg;
  6376.   this.input.value = conversion.arg.text;
  6377.  
  6378.   var prefixLen = 0;
  6379.   if (this.type instanceof JavascriptType) {
  6380.     var typed = conversion.arg.text;
  6381.     var lastDot = typed.lastIndexOf('.');
  6382.     if (lastDot !== -1) {
  6383.       prefixLen = lastDot;
  6384.     }
  6385.   }
  6386.  
  6387.   var items = [];
  6388.   var predictions = conversion.getPredictions();
  6389.   predictions.forEach(function(item) {
  6390.     // Commands can be hidden
  6391.     if (!item.hidden) {
  6392.       items.push({
  6393.         name: item.name.substring(prefixLen),
  6394.         complete: item.name,
  6395.         description: item.description || ''
  6396.       });
  6397.     }
  6398.   }, this);
  6399.  
  6400.   this.menu.show(items);
  6401.   this.setMessage(conversion.message);
  6402. };
  6403.  
  6404. JavascriptField.prototype.onItemClick = function(ev) {
  6405.   this.item = ev.currentTarget.item;
  6406.   this.arg = this.arg.beget(this.item.complete, { normalize: true });
  6407.   var conversion = this.type.parse(this.arg);
  6408.   this.fieldChanged({ conversion: conversion });
  6409.   this.setMessage(conversion.message);
  6410. };
  6411.  
  6412. JavascriptField.prototype.onInputChange = function(ev) {
  6413.   this.item = ev.currentTarget.item;
  6414.   var conversion = this.getConversion();
  6415.   this.fieldChanged({ conversion: conversion });
  6416.   this.setMessage(conversion.message);
  6417. };
  6418.  
  6419. JavascriptField.prototype.getConversion = function() {
  6420.   // This tweaks the prefix/suffix of the argument to fit
  6421.   this.arg = this.arg.beget(this.input.value, { normalize: true });
  6422.   return this.type.parse(this.arg);
  6423. };
  6424.  
  6425. JavascriptField.DEFAULT_VALUE = '__JavascriptField.DEFAULT_VALUE';
  6426.  
  6427. exports.JavascriptField = JavascriptField;
  6428. addField(JavascriptField);
  6429.  
  6430.  
  6431. /**
  6432.  * A field that works with deferred types by delaying resolution until that
  6433.  * last possible time
  6434.  */
  6435. function DeferredField(type, options) {
  6436.   this.document = options.document;
  6437.   this.type = type;
  6438.   this.options = options;
  6439.   this.requisition = options.requisition;
  6440.   this.requisition.assignmentChange.add(this.update, this);
  6441.  
  6442.   this.element = dom.createElement(this.document, 'div');
  6443.   this.update();
  6444.  
  6445.   this.fieldChanged = createEvent('DeferredField.fieldChanged');
  6446. }
  6447.  
  6448. DeferredField.prototype = Object.create(Field.prototype);
  6449.  
  6450. DeferredField.prototype.update = function() {
  6451.   var subtype = this.type.defer();
  6452.   if (subtype === this.subtype) {
  6453.     return;
  6454.   }
  6455.  
  6456.   if (this.field) {
  6457.     this.field.destroy();
  6458.   }
  6459.  
  6460.   this.subtype = subtype;
  6461.   this.field = getField(subtype, this.options);
  6462.   this.field.fieldChanged.add(this.fieldChanged, this);
  6463.  
  6464.   dom.clearElement(this.element);
  6465.   this.element.appendChild(this.field.element);
  6466. };
  6467.  
  6468. DeferredField.claim = function(type) {
  6469.   return type instanceof DeferredType ? Field.MATCH : Field.NO_MATCH;
  6470. };
  6471.  
  6472. DeferredField.prototype.destroy = function() {
  6473.   Field.prototype.destroy.call(this);
  6474.   this.requisition.assignmentChange.remove(this.update, this);
  6475.   delete this.element;
  6476.   delete this.document;
  6477.   delete this.onInputChange;
  6478. };
  6479.  
  6480. DeferredField.prototype.setConversion = function(conversion) {
  6481.   this.field.setConversion(conversion);
  6482. };
  6483.  
  6484. DeferredField.prototype.getConversion = function() {
  6485.   return this.field.getConversion();
  6486. };
  6487.  
  6488. exports.DeferredField = DeferredField;
  6489. addField(DeferredField);
  6490.  
  6491.  
  6492. /**
  6493.  * For use with deferred types that do not yet have anything to resolve to.
  6494.  * BlankFields are not for general use.
  6495.  */
  6496. function BlankField(type, options) {
  6497.   this.document = options.document;
  6498.   this.type = type;
  6499.   this.element = dom.createElement(this.document, 'div');
  6500.  
  6501.   this.fieldChanged = createEvent('BlankField.fieldChanged');
  6502. }
  6503.  
  6504. BlankField.prototype = Object.create(Field.prototype);
  6505.  
  6506. BlankField.claim = function(type) {
  6507.   return type instanceof BlankType ? Field.MATCH : Field.NO_MATCH;
  6508. };
  6509.  
  6510. BlankField.prototype.setConversion = function() { };
  6511.  
  6512. BlankField.prototype.getConversion = function() {
  6513.   return new Conversion(null);
  6514. };
  6515.  
  6516. exports.BlankField = BlankField;
  6517. addField(BlankField);
  6518.  
  6519.  
  6520. /**
  6521.  * Adds add/delete buttons to a normal field allowing there to be many values
  6522.  * given for a parameter.
  6523.  */
  6524. function ArrayField(type, options) {
  6525.   this.document = options.document;
  6526.   this.type = type;
  6527.   this.options = options;
  6528.   this.requ = options.requisition;
  6529.  
  6530.   this._onAdd = this._onAdd.bind(this);
  6531.   this.members = [];
  6532.  
  6533.   // <div class=gcliArrayParent save="${element}">
  6534.   this.element = dom.createElement(this.document, 'div');
  6535.   this.element.className = 'gcliArrayParent';
  6536.  
  6537.   // <button class=gcliArrayMbrAdd onclick="${_onAdd}" save="${addButton}">Add
  6538.   this.addButton = dom.createElement(this.document, 'button');
  6539.   this.addButton.className = 'gcliArrayMbrAdd';
  6540.   this.addButton.addEventListener('click', this._onAdd, false);
  6541.   this.addButton.innerHTML = l10n.lookup('fieldArrayAdd');
  6542.   this.element.appendChild(this.addButton);
  6543.  
  6544.   // <div class=gcliArrayMbrs save="${mbrElement}">
  6545.   this.container = dom.createElement(this.document, 'div');
  6546.   this.container.className = 'gcliArrayMbrs';
  6547.   this.element.appendChild(this.container);
  6548.  
  6549.   this.onInputChange = this.onInputChange.bind(this);
  6550.  
  6551.   this.fieldChanged = createEvent('ArrayField.fieldChanged');
  6552. }
  6553.  
  6554. ArrayField.prototype = Object.create(Field.prototype);
  6555.  
  6556. ArrayField.claim = function(type) {
  6557.   return type instanceof ArrayType ? Field.MATCH : Field.NO_MATCH;
  6558. };
  6559.  
  6560. ArrayField.prototype.destroy = function() {
  6561.   Field.prototype.destroy.call(this);
  6562.   this.addButton.removeEventListener('click', this._onAdd, false);
  6563. };
  6564.  
  6565. ArrayField.prototype.setConversion = function(conversion) {
  6566.   // BUG 653568: this is too brutal - it removes focus from any the current field
  6567.   dom.clearElement(this.container);
  6568.   this.members = [];
  6569.  
  6570.   conversion.conversions.forEach(function(subConversion) {
  6571.     this._onAdd(null, subConversion);
  6572.   }, this);
  6573. };
  6574.  
  6575. ArrayField.prototype.getConversion = function() {
  6576.   var conversions = [];
  6577.   var arrayArg = new ArrayArgument();
  6578.   for (var i = 0; i < this.members.length; i++) {
  6579.     var conversion = this.members[i].field.getConversion();
  6580.     conversions.push(conversion);
  6581.     arrayArg.addArgument(conversion.arg);
  6582.   }
  6583.   return new ArrayConversion(conversions, arrayArg);
  6584. };
  6585.  
  6586. ArrayField.prototype._onAdd = function(ev, subConversion) {
  6587.   // <div class=gcliArrayMbr save="${element}">
  6588.   var element = dom.createElement(this.document, 'div');
  6589.   element.className = 'gcliArrayMbr';
  6590.   this.container.appendChild(element);
  6591.  
  6592.   // ${field.element}
  6593.   var field = getField(this.type.subtype, this.options);
  6594.   field.fieldChanged.add(function() {
  6595.     var conversion = this.getConversion();
  6596.     this.fieldChanged({ conversion: conversion });
  6597.     this.setMessage(conversion.message);
  6598.   }, this);
  6599.  
  6600.   if (subConversion) {
  6601.     field.setConversion(subConversion);
  6602.   }
  6603.   element.appendChild(field.element);
  6604.  
  6605.   // <div class=gcliArrayMbrDel onclick="${_onDel}">
  6606.   var delButton = dom.createElement(this.document, 'button');
  6607.   delButton.className = 'gcliArrayMbrDel';
  6608.   delButton.addEventListener('click', this._onDel, false);
  6609.   delButton.innerHTML = l10n.lookup('fieldArrayDel');
  6610.   element.appendChild(delButton);
  6611.  
  6612.   var member = {
  6613.     element: element,
  6614.     field: field,
  6615.     parent: this
  6616.   };
  6617.   member.onDelete = function() {
  6618.     this.parent.container.removeChild(this.element);
  6619.     this.parent.members = this.parent.members.filter(function(test) {
  6620.       return test !== this;
  6621.     });
  6622.     this.parent.onInputChange();
  6623.   }.bind(member);
  6624.   delButton.addEventListener('click', member.onDelete, false);
  6625.  
  6626.   this.members.push(member);
  6627. };
  6628.  
  6629. exports.ArrayField = ArrayField;
  6630. addField(ArrayField);
  6631.  
  6632.  
  6633. });
  6634. /*
  6635.  * Copyright 2009-2011 Mozilla Foundation and contributors
  6636.  * Licensed under the New BSD license. See LICENSE.txt or:
  6637.  * http://opensource.org/licenses/BSD-3-Clause
  6638.  */
  6639.  
  6640. define('gcli/ui/menu', ['require', 'exports', 'module' , 'gcli/util', 'gcli/types', 'gcli/argument', 'gcli/canon', 'gcli/ui/domtemplate', 'text!gcli/ui/menu.css', 'text!gcli/ui/menu.html'], function(require, exports, module) {
  6641.  
  6642.  
  6643. var dom = require('gcli/util').dom;
  6644.  
  6645. var Conversion = require('gcli/types').Conversion;
  6646. var Argument = require('gcli/argument').Argument;
  6647. var canon = require('gcli/canon');
  6648.  
  6649. var Templater = require('gcli/ui/domtemplate').Templater;
  6650.  
  6651. var menuCss = require('text!gcli/ui/menu.css');
  6652. var menuHtml = require('text!gcli/ui/menu.html');
  6653.  
  6654.  
  6655. /**
  6656.  * Menu is a display of the commands that are possible given the state of a
  6657.  * requisition.
  6658.  * @param document The document from which we create elements.
  6659.  * @param options A way to customize the menu display. Valid options are:
  6660.  * - field:true Turns the menu display into a drop-down for use inside a
  6661.  * JavascriptField.
  6662.  */
  6663. function Menu(document, options) {
  6664.   this.element =  dom.createElement(document, 'div');
  6665.   this.element.className = 'gcliMenu';
  6666.   if (options && options.field) {
  6667.     this.element.className += ' gcliMenuField';
  6668.   }
  6669.  
  6670.   // Pull the HTML into the DOM, but don't add it to the document
  6671.   if (menuCss != null) {
  6672.     this.style = dom.importCss(menuCss, document);
  6673.   }
  6674.  
  6675.   var templates = dom.createElement(document, 'div');
  6676.   dom.setInnerHtml(templates, menuHtml);
  6677.   this.optTempl = templates.querySelector('#gcliOptTempl');
  6678.  
  6679.   // Contains the items that should be displayed
  6680.   this.items = null;
  6681. }
  6682.  
  6683. /**
  6684.  * Avoid memory leaks
  6685.  */
  6686. Menu.prototype.destroy = function() {
  6687.   if (this.style) {
  6688.     this.style.parentNode.removeChild(this.style);
  6689.     delete this.style;
  6690.   }
  6691.  
  6692.   delete this.element;
  6693.   delete this.items;
  6694.   delete this.optTempl;
  6695. };
  6696.  
  6697. /**
  6698.  * The default is to do nothing when someone clicks on the menu.
  6699.  * Plug an implementation in here before calling show() to do something useful.
  6700.  * This is called from template.html
  6701.  * @param ev The click event from the browser
  6702.  */
  6703. Menu.prototype.onItemClick = function(ev) {
  6704. };
  6705.  
  6706. /**
  6707.  * Display a number of items in the menu (or hide the menu if there is nothing
  6708.  * to display)
  6709.  * @param items The items to show in the menu
  6710.  * @param error An error message to display
  6711.  */
  6712. Menu.prototype.show = function(items, error) {
  6713.   this.error = error;
  6714.   this.items = items;
  6715.  
  6716.   if (this.error == null && this.items.length === 0) {
  6717.     this.element.style.display = 'none';
  6718.     return;
  6719.   }
  6720.  
  6721.   var options = this.optTempl.cloneNode(true);
  6722.   new Templater().processNode(options, this);
  6723.  
  6724.   dom.clearElement(this.element);
  6725.   this.element.appendChild(options);
  6726.  
  6727.   this.element.style.display = 'block';
  6728. };
  6729.  
  6730. /**
  6731.  * Hide the menu
  6732.  */
  6733. Menu.prototype.hide = function() {
  6734.   this.element.style.display = 'none';
  6735. };
  6736.  
  6737. exports.Menu = Menu;
  6738.  
  6739.  
  6740. /**
  6741.  * CommandMenu is a special menu that integrates with a Requisition to display
  6742.  * available commands.
  6743.  */
  6744. function CommandMenu(document, requisition) {
  6745.   Menu.call(this, document);
  6746.   this.requisition = requisition;
  6747.  
  6748.   this.requisition.commandChange.add(this.onCommandChange, this);
  6749.   canon.canonChange.add(this.onCommandChange, this);
  6750. }
  6751.  
  6752. CommandMenu.prototype = Object.create(Menu.prototype);
  6753.  
  6754. /**
  6755.  * Avoid memory leaks
  6756.  */
  6757. CommandMenu.prototype.destroy = function() {
  6758.   this.requisition.commandChange.remove(this.onCommandChange, this);
  6759.   canon.canonChange.remove(this.onCommandChange, this);
  6760.  
  6761.   Menu.prototype.destroy.call(this);
  6762. };
  6763.  
  6764. /**
  6765.  * We want to fill-in the clicked command in the cli input when the user clicks
  6766.  */
  6767. CommandMenu.prototype.onItemClick = function(ev) {
  6768.   var type = this.requisition.commandAssignment.param.type;
  6769.  
  6770.   var text = type.stringify(ev.currentTarget.item);
  6771.   var arg = new Argument(text);
  6772.   arg.suffix = ' ';
  6773.  
  6774.   var conversion = type.parse(arg);
  6775.   this.requisition.commandAssignment.setConversion(conversion);
  6776. };
  6777.  
  6778. /**
  6779.  * Update the various hint components to reflect the changed command
  6780.  */
  6781. CommandMenu.prototype.onCommandChange = function(ev) {
  6782.   var command = this.requisition.commandAssignment.getValue();
  6783.   if (!command || !command.exec) {
  6784.     var error = this.requisition.commandAssignment.getMessage();
  6785.     var predictions = this.requisition.commandAssignment.getPredictions();
  6786.  
  6787.     if (predictions.length === 0) {
  6788.       var commandType = this.requisition.commandAssignment.param.type;
  6789.       var conversion = commandType.parse(new Argument());
  6790.       predictions = conversion.getPredictions();
  6791.     }
  6792.  
  6793.     predictions.sort(function(command1, command2) {
  6794.       return command1.name.localeCompare(command2.name);
  6795.     });
  6796.     var items = [];
  6797.     predictions.forEach(function(item) {
  6798.       if (item.description && !item.hidden) {
  6799.         items.push(item);
  6800.       }
  6801.     }, this);
  6802.  
  6803.     this.show(items, error);
  6804.   }
  6805.   else {
  6806.     if (ev && ev.oldValue === ev.newValue) {
  6807.       return; // Just the text has changed
  6808.     }
  6809.  
  6810.     this.hide();
  6811.   }
  6812. };
  6813.  
  6814. exports.CommandMenu = CommandMenu;
  6815.  
  6816.  
  6817. });
  6818. /*
  6819.  * Copyright 2009-2011 Mozilla Foundation and contributors
  6820.  * Licensed under the New BSD license. See LICENSE.txt or:
  6821.  * http://opensource.org/licenses/BSD-3-Clause
  6822.  */
  6823.  
  6824. define('gcli/ui/domtemplate', ['require', 'exports', 'module' ], function(require, exports, module) {
  6825.  
  6826.   Components.utils.import("resource:///modules/devtools/Templater.jsm");
  6827.   exports.Templater = Templater;
  6828.  
  6829. });
  6830. define("text!gcli/ui/menu.css", [], void 0);
  6831. define("text!gcli/ui/menu.html", [], "" +
  6832.   "<!--" +
  6833.   "Template for the beginnings of a command menu." +
  6834.   "This will work with things other than a command - many things are a set of" +
  6835.   "things with a name and description." +
  6836.   "In the command context it is evaluated once for every keypress in the cli" +
  6837.   "when a command has not been entered." +
  6838.   "-->" +
  6839.   "<div id=\"gcliOptTempl\" aria-live=\"polite\">" +
  6840.   "  <div class=\"gcliOption\" foreach=\"item in ${items}\" onclick=\"${onItemClick}\"" +
  6841.   "      title=\"${item.manual || ''}\">" +
  6842.   "    ${__element.item = item; ''}" +
  6843.   "    <span class=\"gcliOptionName\">${item.name}</span>" +
  6844.   "    <span class=\"gcliOptionDesc\">${item.description}</span>" +
  6845.   "  </div>" +
  6846.   "  <div class=\"gcliMenuError\" if=\"${error}\">${error}</div>" +
  6847.   "</div>" +
  6848.   "");
  6849.  
  6850. define("text!gcli/ui/arg_fetch.css", [], void 0);
  6851. define("text!gcli/ui/arg_fetch.html", [], "" +
  6852.   "<!--" +
  6853.   "Template for an Assignment." +
  6854.   "Evaluated each time the commandAssignment changes" +
  6855.   "-->" +
  6856.   "<div id=\"gcliReqTempl\" aria-live=\"polite\">" +
  6857.   "  <div>" +
  6858.   "    <div class=\"gcliCmdDesc\">" +
  6859.   "      ${requisition.commandAssignment.getValue().description}" +
  6860.   "    </div>" +
  6861.   "    <table class=\"gcliParams\">" +
  6862.   "      <tbody class=\"gcliAssignment\"" +
  6863.   "          foreach=\"assignment in ${requisition.getAssignments()}\">" +
  6864.   "        <!-- Parameter -->" +
  6865.   "        <tr class=\"gcliGroupRow\">" +
  6866.   "          <td class=\"gcliParamName\">" +
  6867.   "            <label for=\"gcliForm${assignment.param.name}\">" +
  6868.   "              ${assignment.param.description ? assignment.param.description + ':' : ''}" +
  6869.   "            </label>" +
  6870.   "          </td>" +
  6871.   "          <td class=\"gcliParamInput\">${getInputFor(assignment)}</td>" +
  6872.   "          <td>" +
  6873.   "            <span class=\"gcliRequired\" if=\"${assignment.param.isDataRequired()}\"> *</span>" +
  6874.   "          </td>" +
  6875.   "        </tr>" +
  6876.   "        <tr class=\"gcliGroupRow\">" +
  6877.   "          <td class=\"gcliParamError\" colspan=\"2\">" +
  6878.   "            ${linkMessageElement(assignment, __element)}" +
  6879.   "          </td>" +
  6880.   "        </tr>" +
  6881.   "      </tbody>" +
  6882.   "      <tfoot>" +
  6883.   "        <tr>" +
  6884.   "          <td colspan=\"3\" class=\"gcliParamSubmit\">" +
  6885.   "            <input type=\"submit\" value=\"Cancel\" onclick=\"${onFormCancel}\"/>" +
  6886.   "            <input type=\"submit\" value=\"OK\" onclick=\"${onFormOk}\" save=\"${okElement}\"/>" +
  6887.   "          </td>" +
  6888.   "        </tr>" +
  6889.   "      </tfoot>" +
  6890.   "    </table>" +
  6891.   "  </div>" +
  6892.   "</div>" +
  6893.   "");
  6894.  
  6895. /*
  6896.  * Copyright 2009-2011 Mozilla Foundation and contributors
  6897.  * Licensed under the New BSD license. See LICENSE.txt or:
  6898.  * http://opensource.org/licenses/BSD-3-Clause
  6899.  */
  6900.  
  6901. define('gcli/ui/focus', ['require', 'exports', 'module' , 'gcli/util'], function(require, exports, module) {
  6902.  
  6903.  
  6904. var util = require('gcli/util');
  6905.  
  6906. /**
  6907.  * FocusManager solves the problem of tracking focus among a set of nodes.
  6908.  * The specific problem we are solving is when the hint element must be visible
  6909.  * if either the command line or any of the inputs in the hint element has the
  6910.  * focus, and invisible at other times, without hiding and showing the hint
  6911.  * element even briefly as the focus changes between them.
  6912.  * It does this simply by postponing the hide events by 250ms to see if
  6913.  * something else takes focus.
  6914.  * @param options An optional object containing configuration values. Valid
  6915.  * properties on the options object are:
  6916.  * - document
  6917.  * - blurDelay
  6918.  * - debug
  6919.  * - initialFocus
  6920.  */
  6921. function FocusManager(options) {
  6922.   options = options || {};
  6923.  
  6924.   this._debug = options.debug || false;
  6925.   this._blurDelayTimeout = null; // Result of setTimeout in delaying a blur
  6926.   this._monitoredElements = [];  // See addMonitoredElement()
  6927.  
  6928.   this.hasFocus = false;
  6929.   this.blurDelay = options.blurDelay || 250;
  6930.   this.document = options.document || document;
  6931.  
  6932.   this.onFocus = util.createEvent('FocusManager.onFocus');
  6933.   this.onBlur = util.createEvent('FocusManager.onBlur');
  6934.  
  6935.   // We take a focus event anywhere to be an indication that we might be about
  6936.   // to lose focus
  6937.   this._onDocumentFocus = function() {
  6938.     this.reportBlur('document');
  6939.   }.bind(this);
  6940.   this.document.addEventListener('focus', this._onDocumentFocus, true);
  6941. }
  6942.  
  6943. /**
  6944.  * Avoid memory leaks
  6945.  */
  6946. FocusManager.prototype.destroy = function() {
  6947.   this.document.removeEventListener('focus', this._onDocumentFocus, true);
  6948.   delete this.document;
  6949.  
  6950.   for (var i = 0; i < this._monitoredElements.length; i++) {
  6951.     var monitor = this._monitoredElements[i];
  6952.     console.error('Hanging monitored element: ', monitor.element);
  6953.  
  6954.     monitor.element.removeEventListener('focus', monitor.onFocus, true);
  6955.     monitor.element.removeEventListener('blur', monitor.onBlur, true);
  6956.   }
  6957.  
  6958.   if (this._blurDelayTimeout) {
  6959.     clearTimeout(this._blurDelayTimeout);
  6960.     this._blurDelayTimeout = null;
  6961.   }
  6962. };
  6963.  
  6964. /**
  6965.  * The easy way to include an element in the set of things that are part of the
  6966.  * aggregate focus. Using [add|remove]MonitoredElement() is a simpler way of
  6967.  * option than calling report[Focus|Blur]()
  6968.  * @param element The element on which to track focus|blur events
  6969.  * @param where Optional source string for debugging only
  6970.  */
  6971. FocusManager.prototype.addMonitoredElement = function(element, where) {
  6972.   if (this._debug) {
  6973.     console.log('FocusManager.addMonitoredElement(' + (where || 'unknown') + ')');
  6974.   }
  6975.  
  6976.   var monitor = {
  6977.     element: element,
  6978.     where: where,
  6979.     onFocus: function() { this.reportFocus(where); }.bind(this),
  6980.     onBlur: function() { this.reportBlur(where); }.bind(this)
  6981.   };
  6982.  
  6983.   element.addEventListener('focus', monitor.onFocus, true);
  6984.   element.addEventListener('blur', monitor.onBlur, true);
  6985.   this._monitoredElements.push(monitor);
  6986. };
  6987.  
  6988. /**
  6989.  * Undo the effects of addMonitoredElement()
  6990.  * @param element The element to stop tracking
  6991.  */
  6992. FocusManager.prototype.removeMonitoredElement = function(element) {
  6993.   var monitor;
  6994.   var matchIndex;
  6995.  
  6996.   for (var i = 0; i < this._monitoredElements.length; i++) {
  6997.     if (this._monitoredElements[i].element === element) {
  6998.       monitor = this._monitoredElements[i];
  6999.       matchIndex = i;
  7000.     }
  7001.   }
  7002.  
  7003.   if (!monitor) {
  7004.     if (this._debug) {
  7005.       console.error('Missing monitor for element. ', element);
  7006.     }
  7007.     return;
  7008.   }
  7009.  
  7010.   this._monitoredElements.splice(matchIndex, 1);
  7011.   element.removeEventListener('focus', monitor.onFocus, true);
  7012.   element.removeEventListener('blur', monitor.onBlur, true);
  7013. };
  7014.  
  7015. /**
  7016.  * Some component has received a 'focus' event. This sets the internal status
  7017.  * straight away and informs the listeners
  7018.  * @param where Optional source string for debugging only
  7019.  */
  7020. FocusManager.prototype.reportFocus = function(where) {
  7021.   if (this._debug) {
  7022.     console.log('FocusManager.reportFocus(' + (where || 'unknown') + ')');
  7023.   }
  7024.  
  7025.   if (this._blurDelayTimeout) {
  7026.     if (this._debug) {
  7027.       console.log('FocusManager.cancelBlur');
  7028.     }
  7029.     clearTimeout(this._blurDelayTimeout);
  7030.     this._blurDelayTimeout = null;
  7031.   }
  7032.  
  7033.   if (!this.hasFocus) {
  7034.     this.hasFocus = true;
  7035.     this.onFocus();
  7036.   }
  7037. };
  7038.  
  7039. /**
  7040.  * Some component has received a 'blur' event. This waits for a while to see if
  7041.  * we are going to get any subsequent 'focus' events and then sets the internal
  7042.  * status and informs the listeners
  7043.  * @param where Optional source string for debugging only
  7044.  */
  7045. FocusManager.prototype.reportBlur = function(where) {
  7046.   if (this._debug) {
  7047.     console.log('FocusManager.reportBlur(' + where + ')');
  7048.   }
  7049.  
  7050.   if (this.hasFocus) {
  7051.     if (this._blurDelayTimeout) {
  7052.       if (this._debug) {
  7053.         console.log('FocusManager.blurPending');
  7054.       }
  7055.       return;
  7056.     }
  7057.  
  7058.     this._blurDelayTimeout = setTimeout(function() {
  7059.       if (this._debug) {
  7060.         console.log('FocusManager.blur');
  7061.       }
  7062.       this.hasFocus = false;
  7063.       this.onBlur();
  7064.       this._blurDelayTimeout = null;
  7065.     }.bind(this), this.blurDelay);
  7066.   }
  7067. };
  7068.  
  7069. exports.FocusManager = FocusManager;
  7070.  
  7071.  
  7072. });
  7073.  
  7074. /*
  7075.  * require GCLI so it can be exported as declared in EXPORTED_SYMBOLS
  7076.  * The dependencies specified here should be the same as in Makefile.dryice.js
  7077.  */
  7078. var gcli = require("gcli/index");
  7079.